Load the packages used

library("tidyr")
library("stringr")
library("tibble")
library("pheatmap")
library("Seurat")
Loading required package: ggplot2
Loading required package: cowplot

Attaching package: ‘cowplot’

The following object is masked from ‘package:ggplot2’:

    ggsave

Loading required package: Matrix

Attaching package: ‘Matrix’

The following object is masked from ‘package:tidyr’:

    expand
library("ggrepel")
library("dplyr")

Attaching package: ‘dplyr’

The following object is masked from ‘package:Biobase’:

    combine

The following objects are masked from ‘package:BiocGenerics’:

    combine, intersect, setdiff, union

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library("forcats")

Load all the data

We have three different sequencing experiments and want to cross compare between

  • Bulk sequencing of NSCs, Neuroblasts, Microglia and Endothelial Cells
  • Single Cell RNA sequencing of Neural Stem cells with
  • SMARTseq2 protocol: qNSC1 - aNSC2
  • 10X protocol: qNSC1 - NB + OPC & OD

Define colors for young and old throughout the analysis

age_colors <- c(young = "yellowgreen" , old = "slateblue")
age_colors_bulk <- c(young = "yellowgreen" , old = "slateblue" , Mo7 = "slateblue1")

Load the bulk sequencing TPM values

bulk_data <- read.csv(file = "../Bulk_sequencing/count_table/Gene_expression_matrix_population.csv" , row.names = 1)
bulk_data <- as.matrix(bulk_data)

Log2 the TPM values

bulk_data <- log2(bulk_data+1)

Also we load the annotation

bulk_annotation <- read.csv(file = "../Bulk_sequencing/annotation/annotation_bulk.csv" , row.names = 1)
bulk_annotation$type <- factor(x = bulk_annotation$type , levels = c("NSC","Neuroblast","Microglia","Endothelial"))
bulk_annotation$age <- factor(x = bulk_annotation$age , levels = c("young","Mo7","old"))

Which celltypes do we have?

bulk_celltypes <- unique(as.character(bulk_annotation$type))
bulk_celltypes
[1] "NSC"         "Neuroblast"  "Endothelial" "Microglia"  

Load the SMARTseq2 sequencing TPM values

smartseq2_data <- read.csv(file = "../SmartSeq2/count_table/TPM_NSCs.csv") %>% dplyr::select(-X) %>% column_to_rownames(var = "ensembl_gene_id")
smartseq2_data <- as.matrix(smartseq2_data)

Log2 the TPM values

smartseq2_data <- log2(smartseq2_data+1)

Also we load the annotation

smartseq2_annotation <- read.csv(file = "../SmartSeq2/count_table/NSCs_annotation.csv" , row.names = 1)
smartseq2_annotation$type <- fct_recode(.f = smartseq2_annotation$type , qNSC1 = "q1"  , qNSC2 = "q2" , aNSC1 = "a1" , aNSC2 = "a2")
smartseq2_annotation$type <- factor(x = smartseq2_annotation$type , levels = c("qNSC1","qNSC2","aNSC1","aNSC2"))
smartseq2_annotation$age <- factor(x = smartseq2_annotation$age , levels = c("young","old"))

Which celltypes do we have?

smartseq2_celltype_colors <- c( qNSC1 = "steelblue" , qNSC2 = "steelblue1" , aNSC1 = "tomato" , aNSC2 = "sienna1")

Load the 10X sequencing normalized values

seurat_10X2 <- readRDS(file = "../10X/seurat_10X2_clustered_min_1500_nGene.RDS" )

We can extract the normalized expression from the object

tenx_data <- expm1( seurat_10X2@data )
tenx_data <- as.matrix(tenx_data)

Log2 the TPM values

tenx_data <- log2(tenx_data+1)

Also we extract the annotation from the object

tenx_annotation <- FetchData(object = seurat_10X2 , vars.all = c("celltype","age") ) %>% rownames_to_column("cell") %>% dplyr::rename(type = celltype)
tenx_annotation$type <- factor(x = tenx_annotation$type , levels = c("qNSC1","qNSC2","aNSC0","aNSC1","aNSC2","TAP","NB","OPC","OD"))
tenx_annotation$age <- factor(x = tenx_annotation$age , levels = c("young","old"))

Which celltypes do we have?

tenx_celltype_colors <- c( qNSC1 = "steelblue" , qNSC2 = "steelblue1" , aNSC0 = "tomato" , aNSC1 = "sienna1", aNSC2 = "sienna3" , TAP = "green" , NB = "yellow" , OPC = "pink" , OD = "violet")

Also we can load the genes.tsv file from the CellRanger Output which contains a mapping of ENSEMBL Gene IDs to the Gene names used in the 10X object

tenx_gene_2_ensembl_tbl <- read.table(file = "../10X/count_table/filtered_gene_bc_matrices_mex/mm10/genes.tsv") %>% dplyr::rename( ensembl_gene_id = V1 , gene_symbol = V2 )

We can translate all the gene symbols to ENSEMBL Gene IDS

tenx_ensembl_gene_id <- unlist(lapply( X = rownames(tenx_data) , FUN = function(gene){
    as.character( tenx_gene_2_ensembl_tbl[ which(tenx_gene_2_ensembl_tbl$gene_symbol == gene) , "ensembl_gene_id" ] )[1] 
  } ))

Add the ENSEMBL Gene IDs as rownames

tenx_data_ensembl <- tenx_data
rownames(tenx_data_ensembl) <- tenx_ensembl_gene_id

Load differentially expressed gene lists

Bulk sequencing

NSCs

bulk_DE_genes_NSC <- read.csv(file = "../Bulk_sequencing/DESeq2_results_old_vs_young/GP_DESeq2_results_Old_vs_Yang.csv" , row.names = 1) %>% mutate(gene_symbol = str_to_title(gene_symbol) )
bulk_DE_genes_NSC_Mo7_vs_young <- read.csv(file = "../Bulk_sequencing/DESeq2_results_old_vs_young/GP_DESeq2_results_M7_vs_Young.csv" , row.names = 1)  %>% mutate(gene_symbol = str_to_title(gene_symbol) )

Neuroblasts

bulk_DE_genes_NB <- read.csv(file = "../Bulk_sequencing/DESeq2_results_old_vs_young/PSA_DESeq2_results_Old_vs_Yang.csv" , row.names = 1)  %>% mutate(gene_symbol = str_to_title(gene_symbol) )

Microglia

bulk_DE_genes_Microglia <- read.csv(file = "../Bulk_sequencing/DESeq2_results_old_vs_young/CD11b_DESeq2_results_Old_vs_Yang.csv" , row.names = 1)  %>% mutate(gene_symbol = str_to_title(gene_symbol) )

Endothelial Cells

bulk_DE_genes_Endothelial <- read.csv(file = "../Bulk_sequencing/DESeq2_results_old_vs_young/CD31_DESeq2_results_Old_vs_Yang.csv" , row.names = 1)  %>% mutate(gene_symbol = str_to_title(gene_symbol) )

Make a list of the differentially expressed genes

bulk_DE_genes <- list( NSC = bulk_DE_genes_NSC , NB = bulk_DE_genes_NB , Microglia = bulk_DE_genes_Microglia , Endothelial = bulk_DE_genes_Endothelial )

MA Plots

MAPlot <- function( x , main = "" , highlight_genes = c() , data = NULL , SMARTseq2 = FALSE , labeL_top_genes = FALSE , max_foldChange = 10 , min_baseMean = 0.1  ,  max_baseMean = 1000000 ){
  require(dplyr)
  require(ggplot2)
  require(ggrepel)
  
  if(!is.null(data)){
    rowmean <- rowMeans(data)
    
    rowmean_df <- data.frame(baseMean = as.numeric(rowmean) , gene_symbol = as.character( names(rowmean) ) )
    
    if(SMARTseq2){
      colnames(rowmean_df)[2] <- "ensembl_gene_id"
      
      x <- left_join(x = x, y = rowmean_df , by = "ensembl_gene_id" )
    }else{
      x <- left_join(x = x, y = rowmean_df , by = "gene_symbol" )
    }
  }
  
  x_sig <- filter( x , padj < 0.05 & abs(log2FoldChange) > 1 )
  
  if( labeL_top_genes ){ x_label <- filter( x_sig ,  abs(log2FoldChange) > 4 ) }
  
  x_highlight <- filter( x , gene_symbol %in% highlight_genes )
  
  g <- ggplot(data = x , mapping = aes(x = baseMean , y = log2FoldChange )) + geom_point(color = "grey" ) + geom_point(data = x_sig , color = "red" ) + geom_point(data = x_highlight , color = "black", fill = "cyan"  , size = 2 , pch = 21) 
  
  if( labeL_top_genes ){
    g <- g + geom_text_repel(data = x_label , mapping = aes( x = baseMean , y = log2FoldChange , label = gene_symbol))
  }
  
  g <- g + scale_x_log10(limits = c(min_baseMean, max_baseMean ), expand = c(0, 0)) + ggtitle(main) + ylim(c(-max_foldChange,max_foldChange))
  
  g
}

Bulk

NSC

With Labels
MAPlot(x = bulk_DE_genes_NSC , main = "NSC (19 Month vs 2 Month)" , labeL_top_genes = TRUE )

MAPlot(x = bulk_DE_genes_NSC_Mo7_vs_young, main = "NSC (7 Month vs 2 Month)" , labeL_top_genes = TRUE)

Without Labels
MAPlot(x = bulk_DE_genes_NSC , main = "NSC (19 Month vs 2 Month)" )

MAPlot(x = bulk_DE_genes_NSC_Mo7_vs_young , main = "NSC (7 Month vs 2 Month)")

NB

MAPlot(x = bulk_DE_genes_NB , main = "NB")

MAPlot(x = bulk_DE_genes_NB , main = "NB" , labeL_top_genes = TRUE)

Microglia

MAPlot(x = bulk_DE_genes_Microglia , main = "Microglia")

MAPlot(x = bulk_DE_genes_Microglia , main = "Microglia" , labeL_top_genes = TRUE)

Endothelial cells

MAPlot(x = bulk_DE_genes_Endothelial , main = "Endothelial")

MAPlot(x = bulk_DE_genes_Endothelial , main = "Endothelial" , labeL_top_genes = TRUE)

Heatmaps of gene expression

HeatmapGenes <- function( x , genes = c() , topVar = NULL , main = "" , annotation , annotation_colors , cluster_cols = FALSE , cluster_rows = TRUE , show_rownames = FALSE , show_colnames = FALSE , arrange_by_age = FALSE , keep_non_expressed = FALSE ){
  require("pheatmap")
  require("viridisLite")
  
  # The cells in the annotation are taken as the cells to be plotted
  annotation <- remove_rownames(df = annotation)
  
  if(! arrange_by_age){
    annotation <- annotation %>% arrange( type , age)
  }else{
    annotation <- annotation %>% arrange( age , type)
  }
  
  if("cell" %in% colnames(annotation)){ names_cols <- annotation %>% pull("cell") %>% as.character() }
  if("sample" %in% colnames(annotation)){ names_cols <- annotation %>% pull("sample") %>% as.character() }
  
  # Then the annotation data.frame is prepared as needed for the pheatmap function with the cell names as rownames
  if("cell" %in% colnames(annotation)){annotation <- column_to_rownames(df = annotation , var = "cell")}
  if("sample" %in% colnames(annotation)){annotation <- column_to_rownames(df = annotation , var = "sample")}
  # We set the order of columns of the annotation to be first age then type 
  annotation <- annotation[,c("age","type")]
  
  # We check for the genes from the list that are available in our data
  genes <- as.character(genes)
  genes_avail <- genes[genes %in% rownames(x)]
  
  if(!is.null(topVar)){
    x <- x[,names_cols]
    
    var_genes <- names(sort(apply(X = x, MARGIN = 1, FUN = var), decreasing = TRUE)[1:topVar])
    var_genes <- var_genes[!is.na(var_genes)]
    
    x_mat <- x[var_genes ,] 
  }else{
    # Only take the values for genes that are from the gene list
    x_mat <- x[genes_avail,names_cols]
  }
  # Dicard all the non expressed genes as they would create problems during the clustering process
  if(!keep_non_expressed){
    x <- x[apply(X = x, MARGIN = 1, FUN = var) > 0,]
  }
  if(keep_non_expressed & cluster_rows){warning("Row clustering cannot be used if unexpressed genes are kept!")}
  
  pheatmap(
    mat = x_mat ,
    cluster_rows = cluster_rows , 
    cluster_cols = cluster_cols ,
    color = viridis(n = 100),
    show_rownames = show_rownames,
    show_colnames = show_colnames ,
    clustering_method = "complete", 
    clustering_distance_rows = "euclidean" ,
    border_color = NA,
    scale = "none",
    annotation_col = annotation,
    main = main,
    fontsize = 5,
    annotation_colors = annotation_colors
)
}

SMARTseq2 data

Top Variable Genes in subpopulations

qNSC1: Top 5000 variable genes
HeatmapGenes(x = smartseq2_data , topVar = 5000 , main = "SMARTseq2: qNSC1 young and old Top variable genes" , annotation = filter(smartseq2_annotation , type %in% c("qNSC1")) , annotation_colors = list(age = age_colors , type = smartseq2_celltype_colors ) , cluster_cols = TRUE , cluster_rows = TRUE)
Loading required package: viridisLite

qNSC2: Top 5000 variable genes
HeatmapGenes(x = smartseq2_data , topVar = 5000 , main = "SMARTseq2: qNSC2 young and old Top variable genes" , annotation = filter(smartseq2_annotation , type %in% c("qNSC2")) , annotation_colors = list(age = age_colors , type = smartseq2_celltype_colors ) , cluster_cols = TRUE , cluster_rows = TRUE)

aNSC1: Top 5000 variable genes
HeatmapGenes(x = smartseq2_data , topVar = 5000 , main = "SMARTseq2: aNSC1 young and old Top variable genes" , annotation = filter(smartseq2_annotation , type %in% c("aNSC1")) , annotation_colors = list(age = age_colors , type = smartseq2_celltype_colors ) , cluster_cols = TRUE , cluster_rows = TRUE)

aNSC2: Top 5000 variable genes
HeatmapGenes(x = smartseq2_data , topVar = 5000 , main = "SMARTseq2: aNSC2 young and old Top variable genes" , annotation = filter(smartseq2_annotation , type %in% c("aNSC2")) , annotation_colors = list(age = age_colors , type = smartseq2_celltype_colors ) , cluster_cols = TRUE , cluster_rows = TRUE)

10X data

Top Variable Genes in subpopulations

qNSC1: Top 5000 variable genes
# HeatmapGenes(x = tenx_data_ensembl , topVar = 5000 , main = "10X: qNSC1 young and old Top variable genes" , annotation = filter(tenx_annotation , type %in% c("qNSC1")) , annotation_colors = list(age = age_colors , type = tenx_celltype_colors ) , cluster_cols = TRUE , cluster_rows = TRUE)
qNSC2: Top 5000 variable genes
# HeatmapGenes(x = tenx_data_ensembl , topVar = 5000 , main = "10X: qNSC2 young and old Top variable genes" , annotation = filter(tenx_annotation , type %in% c("qNSC2")) , annotation_colors = list(age = age_colors , type = tenx_celltype_colors ) , cluster_cols = TRUE , cluster_rows = TRUE )
aNSC0: Top 5000 variable genes
# HeatmapGenes(x = tenx_data_ensembl , topVar = 5000 , main = "10X: aNSC0 young and old Top variable genes" , annotation = filter(tenx_annotation , type %in% c("aNSC0")) , annotation_colors = list(age = age_colors , type = tenx_celltype_colors ) , cluster_cols = TRUE , cluster_rows = TRUE)
aNSC1: Top 5000 variable genes
# HeatmapGenes(x = tenx_data_ensembl , topVar = 5000 , main = "10X: aNSC1 young and old Top variable genes" , annotation = filter(tenx_annotation , type %in% c("aNSC1")) , annotation_colors = list(age = age_colors , type = tenx_celltype_colors ) , cluster_cols = TRUE , cluster_rows = TRUE)
aNSC2: Top 5000 variable genes
# HeatmapGenes(x = tenx_data_ensembl , topVar = 5000 , main = "10X: aNSC2 young and old Top variable genes" , annotation = filter(tenx_annotation , type %in% c("aNSC2")) , annotation_colors = list(age = age_colors , type = tenx_celltype_colors ) , cluster_cols = TRUE , cluster_rows = TRUE)
TAP: Top 5000 variable genes
# HeatmapGenes(x = tenx_data_ensembl , topVar = 5000 , main = "10X: TAP young and old Top variable genes" , annotation = filter(tenx_annotation , type %in% c("TAP")) , annotation_colors = list(age = age_colors , type = tenx_celltype_colors ) , cluster_cols = TRUE , cluster_rows = TRUE)
NB: Top 5000 variable genes
# HeatmapGenes(x = tenx_data_ensembl , topVar = 5000 , main = "10X: NB young and old Top variable genes" , annotation = filter(tenx_annotation , type %in% c("NB")) , annotation_colors = list(age = age_colors , type = tenx_celltype_colors ) , cluster_cols = TRUE , cluster_rows = TRUE)
OPC: Top 5000 variable genes
# HeatmapGenes(x = tenx_data_ensembl , topVar = 5000 , main = "10X: OPC young and old Top variable genes" , annotation = filter(tenx_annotation , type %in% c("OPC")) , annotation_colors = list(age = age_colors , type = tenx_celltype_colors ) , cluster_cols = TRUE , cluster_rows = TRUE)
OD: Top 5000 variable genes
# HeatmapGenes(x = tenx_data_ensembl , topVar = 5000 , main = "10X: OD young and old Top variable genes" , annotation = filter(tenx_annotation , type %in% c("OD")) , annotation_colors = list(age = age_colors , type = tenx_celltype_colors ) , cluster_cols = TRUE , cluster_rows = TRUE)

Compare pseudo-bulk Single Cell RNA-seq with bulk samples - mean TPM values

Pseudobulk_bulk_compare <- function( pseudobulk_data = NULL , pseudobulk_celltype = "x" , bulk_celltype = "y" , bulk_data = NULL , annotation_pseudobulk = NULL , annotation_bulk = NULL , celltype_bulk = NULL  , main = "" ){
  require("ggplot2")
  require("dplyr")
  require("tibble")
  
  if( is.null(pseudobulk_data) | is.null(bulk_data) ){ stop("You need to supply all data !") }
  if( is.null(annotation_pseudobulk) | is.null(annotation_pseudobulk) ){stop("You need to supply the annotation !")}
  
  
  ## Strip Log2 transform of the data
  pseudobulk_data <- 2^(pseudobulk_data)-1
  bulk_data <- 2^(bulk_data)-1
  
  pseudobulk_data <- pseudobulk_data[!is.na(rownames(pseudobulk_data)),]
  bulk_data <- bulk_data[!is.na(rownames(bulk_data)),]
  
  pseudobulk_data <- pseudobulk_data[,annotation_pseudobulk$cell]
  bulk_data <- bulk_data[,annotation_bulk$sample]
    
  pseudobulk_data_comb <- data.frame( expr_x = log2(rowMeans(pseudobulk_data)+1) , ensembl_gene_id = rownames(pseudobulk_data) )
  bulk_data_comb <- data.frame( expr_y = log2(rowMeans(bulk_data)+1) , ensembl_gene_id = rownames(bulk_data) )
  x <- full_join(x = pseudobulk_data_comb , y = bulk_data_comb , by = "ensembl_gene_id" )
  
  g <- ggplot(data = x , mapping = aes(x = expr_x , y = expr_y)) + geom_point(alpha = 0.5) + coord_equal() + labs(x = pseudobulk_celltype , y = bulk_celltype ) + ggtitle(main)
  
  g
  }

SMARTseq2

Pseudobulk_bulk_compare(pseudobulk_data = smartseq2_data , bulk_data = bulk_data , annotation_pseudobulk = filter(smartseq2_annotation, type %in% c("qNSC1","qNSC2","aNSC1","aNSC2"), age == "young") , annotation_bulk = filter(bulk_annotation, type %in% c("NSC") , age == "young"), pseudobulk_celltype = "pseudo-bulk samples: NSC (log2(TPM+1))" , bulk_celltype = " bulk samples: NSC (log2(TPM+1))" , main = "Young NSC: pseudo-bulk vs bulk" )
Column `ensembl_gene_id` joining factors with different levels, coercing to character vector

Pseudobulk_bulk_compare(pseudobulk_data = smartseq2_data , bulk_data = bulk_data , annotation_pseudobulk = filter(smartseq2_annotation, type %in% c("qNSC1","qNSC2","aNSC1","aNSC2"), age == "old") , annotation_bulk = filter(bulk_annotation, type %in% c("NSC") , age == "young"), pseudobulk_celltype = "pseudo-bulk samples: NSC (log2(TPM+1))" , bulk_celltype = " bulk samples: NSC (log2(TPM+1))" , main = "NSC: old pseudo-bulk (SMARTseq2) vs young bulk" )
Column `ensembl_gene_id` joining factors with different levels, coercing to character vector

Pseudobulk_bulk_compare(pseudobulk_data = smartseq2_data , bulk_data = bulk_data , annotation_pseudobulk = filter(smartseq2_annotation, type %in% c("qNSC1","qNSC2","aNSC1","aNSC2"), age == "young") , annotation_bulk = filter(bulk_annotation, type %in% c("NSC") , age == "old"), pseudobulk_celltype = "pseudo-bulk samples: NSC (log2(TPM+1))" , bulk_celltype = " bulk samples: NSC (log2(TPM+1))" , main = "NSC: young pseudo-bulk (SMARTseq2) vs old bulk" )
Column `ensembl_gene_id` joining factors with different levels, coercing to character vector

Pseudobulk_bulk_compare(pseudobulk_data = smartseq2_data , bulk_data = bulk_data , annotation_pseudobulk = filter(smartseq2_annotation, type %in% c("qNSC1","qNSC2","aNSC1","aNSC2"), age == "old") , annotation_bulk = filter(bulk_annotation, type %in% c("NSC") , age == "old"), pseudobulk_celltype = "pseudo-bulk samples: NSC (log2(TPM+1))" , bulk_celltype = " bulk samples: NSC (log2(TPM+1))" , main = "Old NSC: pseudo-bulk (SMARTseq2) vs bulk" )
Column `ensembl_gene_id` joining factors with different levels, coercing to character vector

10X

Pseudobulk_bulk_compare(pseudobulk_data = tenx_data_ensembl , bulk_data = bulk_data , annotation_pseudobulk = filter(tenx_annotation, type %in% c("qNSC1","qNSC2","aNSC0","aNSC1","aNSC2"), age == "old") , annotation_bulk = filter(bulk_annotation, type %in% c("NSC") , age == "old"), pseudobulk_celltype = "pseudo-bulk samples: NSC (log2(expression+1))" , bulk_celltype = " bulk samples: NSC (log2(TPM+1))" , main = "Old NSC: pseudo-bulk (10X) vs bulk" )
Column `ensembl_gene_id` joining factors with different levels, coercing to character vector

Pseudobulk_bulk_compare(pseudobulk_data = tenx_data_ensembl , bulk_data = bulk_data , annotation_pseudobulk = filter(tenx_annotation, type %in% c("qNSC1","qNSC2","aNSC0","aNSC1","aNSC2"), age == "old") , annotation_bulk = filter(bulk_annotation, type %in% c("NSC") , age == "young"), pseudobulk_celltype = "pseudo-bulk samples: NSC (log2(expression+1))" , bulk_celltype = " bulk samples: NSC (log2(TPM+1))" , main = "NSC: Old pseudo-bulk (10X) vs young bulk" )
Column `ensembl_gene_id` joining factors with different levels, coercing to character vector

Pseudobulk_bulk_compare(pseudobulk_data = tenx_data_ensembl , bulk_data = bulk_data , annotation_pseudobulk = filter(tenx_annotation, type %in% c("qNSC1","qNSC2","aNSC0","aNSC1","aNSC2"), age == "young") , annotation_bulk = filter(bulk_annotation, type %in% c("NSC") , age == "old"), pseudobulk_celltype = "pseudo-bulk samples: NSC (log2(TPM+1))" , bulk_celltype = " bulk samples: NSC (log2(expression+1))" , main = "NSC: Young pseudo-bulk (10X) vs old bulk" )
Column `ensembl_gene_id` joining factors with different levels, coercing to character vector

Pseudobulk_bulk_compare(pseudobulk_data = tenx_data_ensembl , bulk_data = bulk_data , annotation_pseudobulk = filter(tenx_annotation, type %in% c("qNSC1","qNSC2","aNSC0","aNSC1","aNSC2"), age == "young") , annotation_bulk = filter(bulk_annotation, type %in% c("NSC") , age == "young"), pseudobulk_celltype = "pseudo-bulk samples: NSC (log2(TPM+1))" , bulk_celltype = " bulk samples: NSC (log2(expression+1))" , main = "Young NSC: pseudo-bulk (10X) vs bulk" )
Column `ensembl_gene_id` joining factors with different levels, coercing to character vector


Pseudobulk MA Plot

Pseudobulk_bulk_MAPlot <- function( pseudobulk_data = NULL , annotation_pseudobulk = NULL  , main = "" , labeL_top_genes = FALSE , highlight_genes = c() , max_foldChange = 10  , max_baseMean = 1000000 ){
  require("ggplot2")
  require("dplyr")
  require("tibble")
  
  ## Strip Log2 transform of the data
  pseudobulk_data <- 2^(pseudobulk_data) - 1
  bulk_data <- 2^(bulk_data) - 1
  if( is.null(pseudobulk_data) ){ stop("You need to supply all data !") }
  if( is.null(annotation_pseudobulk) ){stop("You need to supply the annotation !")}
  
  annotation_pseudobulk_old <- filter(annotation_pseudobulk , age == "old")
  annotation_pseudobulk_young <- filter(annotation_pseudobulk , age == "young")
  pseudobulk_data <- pseudobulk_data[!is.na(rownames(pseudobulk_data)),]
    
  pseudobulk_data_old <- pseudobulk_data[,as.character(annotation_pseudobulk_old$cell)]
  pseudobulk_data_young <- pseudobulk_data[,as.character(annotation_pseudobulk_young$cell)]
  
  pseudobulk_data_complete <- pseudobulk_data[, c( as.character(annotation_pseudobulk_old$cell),as.character(annotation_pseudobulk_young$cell)) ]
  
  if( ! all(rownames(pseudobulk_data_old) == rownames(pseudobulk_data_young))){stop("Rownames are not correct: Pseudobulk")}
  pseudobulk_data_comb <- data.frame( log2FoldChange = log2(((rowSums(pseudobulk_data_old) + 1) / (rowSums(pseudobulk_data_young) + 1) ) )  , expr_young_pseudobulk = rowSums(pseudobulk_data_young) , baseMean = rowMeans(pseudobulk_data_complete),  expr_old_pseudobulk = rowSums(pseudobulk_data_old) , gene = rownames(pseudobulk_data_old) )
  
  pseudobulk_data_comb$log2FoldChange[is.nan(pseudobulk_data_comb$log2FoldChange)] <- 0
  
  x_highlight <- filter(pseudobulk_data_comb ,gene %in% highlight_genes )
  
  g <- ggplot(data = pseudobulk_data_comb , mapping = aes(x = baseMean , y = log2FoldChange)) +  labs(x = "baseMean"  , y = "Log2FoldChange" ) + ggtitle(main) + geom_vline(xintercept = 0) + geom_hline(yintercept = 0) + geom_point(color = "grey" ) + geom_point(data = x_highlight , color = "black", fill = "cyan"  , size = 2 , pch = 21) 
  
  if( labeL_top_genes ){
      g <- g + geom_text_repel(data = x_label , mapping = aes( x = baseMean , y = log2FoldChange , label = gene)) 
  }
  
  g <- g + scale_x_log10(limits = c(0.00001, max_baseMean ), expand = c(0, 0)) + ggtitle(main) + ylim(c(-max_foldChange,max_foldChange))
  
  g
  }
Pseudobulk_bulk_MAPlot(pseudobulk_data = smartseq2_data , annotation_pseudobulk = smartseq2_annotation)

Pseudobulk_bulk_MAPlot(pseudobulk_data = tenx_data , annotation_pseudobulk = dplyr::filter(tenx_annotation, type %in% c("qNSC1","qNSC2","aNSC0","aNSC1","aNSC2")) )

Plot individual genes in the single cell datasets

Beeswarm plot of Il33

SMARTseq2

IL33_smartseq2_data <- smartseq2_data["ENSMUSG00000024810",]
IL33_smartseq2_df <- data.frame( expression = IL33_smartseq2_data , row.names = names(IL33_smartseq2_data)  ) %>% rownames_to_column("cell") %>% left_join( smartseq2_annotation , by = "cell")
Column `cell` joining character vector and factor, coercing into character vector
addmargins( table( IL33_smartseq2_df$age  , IL33_smartseq2_df$expression > 2.5 ) )
       
        FALSE TRUE Sum
  young    73   16  89
  old      71   62 133
  Sum     144   78 222
library(ggbeeswarm)
ggplot(data = IL33_smartseq2_df , mapping = aes(x = age , y = expression , color = type )) + geom_quasirandom( size = 1.5 ,method = "smiley" ) + xlab("Age") + ylab("log2(TPM+1)") + ggtitle("Expression of Il33 in the SMARTseq2 data") + scale_x_discrete(labels = c(young = "2 month" , old = "22 month") ) + scale_color_manual( name = "Activation state" , values = smartseq2_celltype_colors ) + guides(color = guide_legend(override.aes=list(size=1.5)))

10X

IL33_10X_data <- tenx_data_ensembl["ENSMUSG00000024810",]
tenx_annotation_nscs <- tenx_annotation %>% filter( type %in% c("qNSC1","qNSC2","aNSC0","aNSC1","aNSC2"))
IL33_10X_df <- data.frame( expression = IL33_10X_data , row.names = names(IL33_10X_data)  ) %>% rownames_to_column("cell") %>% filter(cell %in% as.character( tenx_annotation_nscs$cell ) ) %>% left_join( tenx_annotation_nscs , by = "cell")
addmargins( table( IL33_10X_df$age  , IL33_10X_df$expression > 0.5 ) )
       
        FALSE TRUE  Sum
  young  1162   63 1225
  old     836  185 1021
  Sum    1998  248 2246
library(ggbeeswarm)
ggplot(data = IL33_10X_df , mapping = aes(x = age , y = expression , color = type )) + xlab("Age") + ylab("log2(expression+1)") + ggtitle("Expression of Il33 in the 10X data") + scale_x_discrete(labels = c(young = "2 month" , old = "22 month") ) + scale_color_manual( name = "Activation state" , values = tenx_celltype_colors ) + guides(color = guide_legend(override.aes=list(size=1.5))) + geom_quasirandom( size = 0.5 ,method = "smiley" ) 


Search for genes from Gene Lists in all datasets

InnateDB genes - manually curated from the bulk seq data (from sig. genes with log2FoldChange > 1)

DE_genes_NSCs_bulk_up_InnateDB <- read.csv("GeneLists/Manually_curated_DE_genes_from_bulk/InnateDB_Gene_list/InnateDB_NSCs.csv" , stringsAsFactors = FALSE , header = TRUE)
library(biomaRt)
mart <- useMart(biomart = "ensembl", dataset = "mmusculus_gene_ensembl")
genes_symbol_ensembl <- getBM(attributes = c("mgi_symbol","ensembl_gene_id" ), filters = "mgi_symbol" , values = DE_genes_NSCs_bulk_up_InnateDB$Genes , mart = mart  )
genes_symbol_ensembl <- dplyr::rename(genes_symbol_ensembl , MGI.symbol = mgi_symbol , gene = ensembl_gene_id )
genes_symbol_ensembl <-  dplyr::filter( genes_symbol_ensembl , gene %in% as.character(bulk_DE_genes_NSC %>% filter(padj < 0.05 , log2FoldChange > 1) %>% pull("ensembl_gene_id") ) )
DE_genes_NSCs_bulk_up_InnateDB <- genes_symbol_ensembl
gene_set <- DE_genes_NSCs_bulk_up_InnateDB
plot_title <- "NSC bulk seq (top DE genes between old and young) annotated in InnateDB"

Bulk

Bulk sequencing: Heatmap

HeatmapGenes(x = bulk_data , genes = unique(gene_set$gene) , main = paste("Bulk:", plot_title) , annotation = bulk_annotation , show_colnames = FALSE , annotation_colors = list(age = age_colors_bulk ) )

Bulk sequencing: MA-Plot

NSC
MAPlot(x = bulk_DE_genes_NSC , main = "NSC (19 month vs 2 month)" , highlight_genes = unique(gene_set$MGI.symbol) ) 

MAPlot(x = bulk_DE_genes_NSC_Mo7_vs_young , main = "NSC (7 Month vs 2 Month)" , highlight_genes = unique(gene_set$MGI.symbol) ) 

NB
MAPlot(x = bulk_DE_genes_NB , main = "NB" , highlight_genes = unique(gene_set$MGI.symbol) ) 

Microglia
MAPlot(x = bulk_DE_genes_Microglia , main = "Microglia" , highlight_genes = unique(gene_set$MGI.symbol) ) 

Endothelial cells
MAPlot(x = bulk_DE_genes_Endothelial , main = "Endothelial" , highlight_genes = unique(gene_set$MGI.symbol) ) 

Pseudobulk SMARTseq2
Pseudobulk_bulk_MAPlot(pseudobulk_data = smartseq2_data , annotation_pseudobulk = smartseq2_annotation , highlight_genes = unique(gene_set$gene) , main = "Pseudobulk: SMARTseq2")

Pseudobulk 10X
Pseudobulk_bulk_MAPlot(pseudobulk_data = tenx_data , annotation_pseudobulk = dplyr::filter(tenx_annotation, type %in% c("qNSC1","qNSC2","aNSC0","aNSC1","aNSC2")) , highlight_genes = unique(gene_set$MGI.symbol) , main = "Pseudobulk: 10X")

SMARTseq2

SMARTseq2 sequencing: Heatmap

Arranged by celltype
 HeatmapGenes(x = smartseq2_data , genes = unique(gene_set$gene) , main = paste("SMARTseq2:", plot_title) , annotation = smartseq2_annotation , annotation_colors = list(age = age_colors , type = smartseq2_celltype_colors )  )

Columns clustered
 test <- HeatmapGenes(x = smartseq2_data , genes = unique(gene_set$gene) , main = paste("SMARTseq2:", plot_title) , annotation = smartseq2_annotation %>% dplyr::filter(type %in% c("qNSC1","qNSC2")) , annotation_colors = list(age = age_colors , type = smartseq2_celltype_colors ) , cluster_cols = TRUE )

10X

10X sequencing: Heatmap

HeatmapGenes(x = tenx_data_ensembl , genes = unique(gene_set$gene) , main = paste("10X:", plot_title) , annotation = tenx_annotation , annotation_colors = list(age = age_colors , type = tenx_celltype_colors ) )

HeatmapGenes(x = tenx_data_ensembl , genes = unique(gene_set$gene) , main = paste("10X:", plot_title) , annotation = tenx_annotation , annotation_colors = list(age = age_colors , type = tenx_celltype_colors ) , cluster_cols = TRUE )

InnateDB genes - manually curated from the bulk seq data (from sig. genes with log2FoldChange > 1) - from Endothelial, NB and Microglia

Endothelial

DE_genes_Endothelial_bulk_up_InnateDB <- read.csv("GeneLists/Manually_curated_DE_genes_from_bulk/InnateDB_Gene_list/InnateDB_Endos.csv" , stringsAsFactors = FALSE , header = TRUE)
library(biomaRt)
mart <- useMart(biomart = "ensembl", dataset = "mmusculus_gene_ensembl")
genes_symbol_ensembl <- getBM(attributes = c("mgi_symbol","ensembl_gene_id" ), filters = "mgi_symbol" , values = DE_genes_Endothelial_bulk_up_InnateDB$Genes , mart = mart  )
genes_symbol_ensembl <- dplyr::rename(genes_symbol_ensembl , MGI.symbol = mgi_symbol , gene = ensembl_gene_id )
genes_symbol_ensembl <-  dplyr::filter( genes_symbol_ensembl , gene %in% as.character(bulk_DE_genes_Endothelial %>% filter(padj < 0.05 , log2FoldChange > 1) %>% pull("ensembl_gene_id") ) )
DE_genes_Endothelial_bulk_up_InnateDB <- genes_symbol_ensembl
MAPlot(x = bulk_DE_genes_Endothelial , main = "Endothelial" , highlight_genes = unique(DE_genes_Endothelial_bulk_up_InnateDB$MGI.symbol) ) 

Microglia

DE_genes_Microglia_bulk_up_InnateDB <- read.csv("GeneLists/Manually_curated_DE_genes_from_bulk/InnateDB_Gene_list/InnateDB_Microglia.csv" , stringsAsFactors = FALSE , header = TRUE)
library(biomaRt)
mart <- useMart(biomart = "ensembl", dataset = "mmusculus_gene_ensembl")
genes_symbol_ensembl <- getBM(attributes = c("mgi_symbol","ensembl_gene_id" ), filters = "mgi_symbol" , values = DE_genes_Microglia_bulk_up_InnateDB$Genes , mart = mart  )
genes_symbol_ensembl <- dplyr::rename(genes_symbol_ensembl , MGI.symbol = mgi_symbol , gene = ensembl_gene_id )
genes_symbol_ensembl <-  dplyr::filter( genes_symbol_ensembl , gene %in% as.character(bulk_DE_genes_Microglia %>% dplyr::filter(padj < 0.05 , log2FoldChange > 1) %>% pull("ensembl_gene_id") ) )
DE_genes_Microglia_bulk_up_InnateDB <- genes_symbol_ensembl
MAPlot(x = bulk_DE_genes_Microglia , main = "Microglia" , highlight_genes = unique(DE_genes_Microglia_bulk_up_InnateDB$MGI.symbol) ) 

NB

DE_genes_NB_bulk_up_InnateDB <- read.csv("GeneLists/Manually_curated_DE_genes_from_bulk/InnateDB_Gene_list/InnateDB_Neuroblasts.csv" , stringsAsFactors = FALSE , header = TRUE)
library(biomaRt)
mart <- useMart(biomart = "ensembl", dataset = "mmusculus_gene_ensembl")
genes_symbol_ensembl <- getBM(attributes = c("mgi_symbol","ensembl_gene_id" ), filters = "mgi_symbol" , values = DE_genes_NB_bulk_up_InnateDB$Genes , mart = mart  )
genes_symbol_ensembl <- dplyr::rename(genes_symbol_ensembl , MGI.symbol = mgi_symbol , gene = ensembl_gene_id )
genes_symbol_ensembl <-  dplyr::filter( genes_symbol_ensembl , gene %in% as.character(bulk_DE_genes_NB %>% dplyr::filter(padj < 0.05 , log2FoldChange > 1) %>% pull("ensembl_gene_id") ) )
DE_genes_NB_bulk_up_InnateDB <- genes_symbol_ensembl
MAPlot(x = bulk_DE_genes_NB , main = "Neuroblast" , highlight_genes = unique(DE_genes_NB_bulk_up_InnateDB$MGI.symbol) ) 

Load raw counts

We can also compare the raw counts for specific genes that we recover with the different sequencing methods

Bulk sequencing data

data_bulk <- read.csv(file = "raw_counts/compatible_rownames_only_NSCs/Bulk_seq_All.genes.counts_compat.csv" , row.names = 1)
anno_bulk <- read.csv(file = "raw_counts/annotation_bulk.csv" , row.names = 1)

Check if all cells from the annotation are present in the data as columns and the other way around

all(colnames(data_bulk) == anno_bulk$name)
[1] TRUE

SMARTseq2 data

data_SMARTseq2_NSCs <- read.csv(file = "raw_counts/compatible_rownames_only_NSCs/SMARTseq2_All.genes.counts_compat.csv" , row.names = 1)
anno_SMARTseq2_NSCs <- read.csv(file = "raw_counts/annotation_NSCs_SMARTseq2.csv" , row.names = 1)

Check if all cells from the annotation are present in the data as columns and the other way around

all(colnames(data_SMARTseq2_NSCs) == anno_SMARTseq2_NSCs$name)
[1] TRUE

10X data

data_10X_NSCs <- read.csv(file = "raw_counts/compatible_rownames_only_NSCs/10X_All.genes.counts_compat.csv" , row.names = 1)
anno_10X_NSCs <- read.csv(file = "raw_counts/annotation_10x_NSCs.csv" , row.names = 1)
colnames(data_10X_NSCs) <- sub(x = colnames(data_10X_NSCs) , pattern = "\\." , replacement = "-" )

Check if all cells from the annotation are present in the data as columns and the other way around

all(colnames(data_10X_NSCs) == anno_10X_NSCs$name)
[1] TRUE

Check raw counts for highly upregulated genes in bulk NSC old to young comparison

10X

How many counts do genes have in the Single cell data sets, that are significantly upregulated in the bulk NSCs

cells_NSCs_10X <- as.character(anno_10X_NSCs$name)

Iigp1 - ENSMUSG00000054072

Iigp1_df <- data.frame( totalReads = colSums(data_10X_NSCs) , geneReads = as.numeric(data_10X_NSCs["ENSMUSG00000054072",cells_NSCs_10X]) , gene = "Iigp1"   )  %>% rownames_to_column("name") %>% left_join(anno_10X_NSCs , by = "name" )
Column `name` joining character vector and factor, coercing into character vector
addmargins( table( Iigp1_df$age  , Iigp1_df$geneReads > 0 ) )
       
        FALSE TRUE  Sum
  old     996   25 1021
  young  1221    4 1225
  Sum    2217   29 2246
gg_Iigp1 <- ggplot(data = Iigp1_df , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") )
gg_Iigp1

Ddx60 - ENSMUSG00000037921

Ddx60_df <- data.frame( totalReads = colSums(data_10X_NSCs) , geneReads = as.numeric(data_10X_NSCs["ENSMUSG00000037921",cells_NSCs_10X]) , gene = "Ddx60"   ) %>% rownames_to_column("name") %>% left_join(anno_10X_NSCs , by = "name" )
Column `name` joining character vector and factor, coercing into character vector
addmargins( table( Ddx60_df$age  , Ddx60_df$geneReads > 0 ) )
       
        FALSE TRUE  Sum
  old    1002   19 1021
  young  1224    1 1225
  Sum    2226   20 2246
gg_Ddx60 <- ggplot(data = Ddx60_df , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") )

Ifit1 - ENSMUSG00000078920

Ifit1_df <- data.frame( totalReads = colSums(data_10X_NSCs) , geneReads = as.numeric(data_10X_NSCs["ENSMUSG00000078920",cells_NSCs_10X]) , gene = "Ifit1" ) %>% rownames_to_column("name") %>% left_join(anno_10X_NSCs , by = "name" )
Column `name` joining character vector and factor, coercing into character vector
addmargins( table( Ifit1_df$age  , Ifit1_df$geneReads > 0 ) )
       
        FALSE TRUE  Sum
  old    1017    4 1021
  young  1223    2 1225
  Sum    2240    6 2246
gg_Ifit1 <- ggplot(data = Ifit1_df , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") )
gg_Ifit1

Rsad2 - ENSMUSG00000020641

Rsad2_df <- data.frame( totalReads = colSums(data_10X_NSCs) , geneReads = as.numeric(data_10X_NSCs["ENSMUSG00000020641",cells_NSCs_10X]) , gene = "Rsad2"  ) %>% rownames_to_column("name") %>% left_join(anno_10X_NSCs , by = "name" )
Column `name` joining character vector and factor, coercing into character vector
addmargins( table( Rsad2_df$age  , Rsad2_df$geneReads > 0 ) )
       
        FALSE TRUE  Sum
  old     995   26 1021
  young  1213   12 1225
  Sum    2208   38 2246
gg_Rsad2 <- ggplot(data = Rsad2_df , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") )
gg_Rsad2

C3 - ENSMUSG00000024164

C3_df <- data.frame( totalReads = colSums(data_10X_NSCs) , geneReads = as.numeric(data_10X_NSCs["ENSMUSG00000024164",cells_NSCs_10X]) , gene = "C3"   )  %>% rownames_to_column("name") %>% left_join(anno_10X_NSCs , by = "name" )
Column `name` joining character vector and factor, coercing into character vector
addmargins( table( C3_df$age  , C3_df$geneReads > 0 ) )
       
        FALSE  Sum
  old    1021 1021
  young  1225 1225
  Sum    2246 2246
gg_C3 <- ggplot(data = C3_df , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") )
gg_C3

Ifi44 - ENSMUSG00000028037

Ifi44_df <- data.frame( totalReads = colSums(data_10X_NSCs) , geneReads = as.numeric(data_10X_NSCs["ENSMUSG00000028037",cells_NSCs_10X]) , gene = "Ifi44"   )  %>% rownames_to_column("name") %>% left_join(anno_10X_NSCs , by = "name" )
Column `name` joining character vector and factor, coercing into character vector
addmargins( table( Ifi44_df$age  , Ifi44_df$geneReads > 0 ) )
       
        FALSE TRUE  Sum
  old    1019    2 1021
  young  1223    2 1225
  Sum    2242    4 2246
gg_Ifi44 <- ggplot(data = Ifi44_df , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") )
gg_Ifi44

Cxcl10 - ENSMUSG00000034855

Cxcl10_df <- data.frame( totalReads = colSums(data_10X_NSCs) , geneReads = as.numeric(data_10X_NSCs["ENSMUSG00000034855",cells_NSCs_10X]) , gene = "Cxcl10"   )  %>% rownames_to_column("name") %>% left_join(anno_10X_NSCs , by = "name" )
Column `name` joining character vector and factor, coercing into character vector
addmargins( table( Cxcl10_df$age  , Cxcl10_df$geneReads > 0 ) )
       
        FALSE TRUE  Sum
  old     954   67 1021
  young  1154   71 1225
  Sum    2108  138 2246
gg_Cxcl10 <- ggplot(data = Cxcl10_df , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") )
gg_Cxcl10

Il33 - ENSMUSG00000024810

Il33_df <- data.frame( totalReads = colSums(data_10X_NSCs) , geneReads = as.numeric(data_10X_NSCs["ENSMUSG00000024810",cells_NSCs_10X]) , gene = "Il33"   )  %>% rownames_to_column("name") %>% left_join(anno_10X_NSCs , by = "name" )
Column `name` joining character vector and factor, coercing into character vector
addmargins( table( Il33_df$age  , Il33_df$geneReads > 0 ) )
       
        FALSE TRUE  Sum
  old     836  185 1021
  young  1162   63 1225
  Sum    1998  248 2246
gg_Il33 <- ggplot(data = Il33_df , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") )
gg_Il33

Ifi47 - ENSMUSG00000078920

Ifi47_df <- data.frame( totalReads = colSums(data_10X_NSCs) , geneReads = as.numeric(data_10X_NSCs["ENSMUSG00000078920",cells_NSCs_10X]) , gene = "Ifi47"   ) %>% rownames_to_column("name") %>% left_join(anno_10X_NSCs , by = "name" )
Column `name` joining character vector and factor, coercing into character vector
addmargins( table( Ifi47_df$age  , Ifi47_df$geneReads > 0 ) )
       
        FALSE TRUE  Sum
  old    1017    4 1021
  young  1223    2 1225
  Sum    2240    6 2246
gg_Ifi47 <- ggplot(data = Ifi47_df , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") )
gg_Ifi47

Gm4951 - ENSMUSG00000073555

Gm4951_df <- data.frame( totalReads = colSums(data_10X_NSCs) , geneReads = as.numeric(data_10X_NSCs["ENSMUSG00000073555",cells_NSCs_10X]) , gene = "Gm4951"   ) %>% rownames_to_column("name") %>% left_join(anno_10X_NSCs , by = "name" )
Column `name` joining character vector and factor, coercing into character vector
addmargins( table( Gm4951_df$age  , Gm4951_df$geneReads > 0 ) )
       
        FALSE TRUE  Sum
  old     993   28 1021
  young  1223    2 1225
  Sum    2216   30 2246
gg_Gm4951 <- ggplot(data = Gm4951_df , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") )
gg_Gm4951

Slc1a3 - ENSMUSG00000005360

Slc1a3_df <- data.frame( totalReads = colSums(data_10X_NSCs) , geneReads = as.numeric(data_10X_NSCs["ENSMUSG00000005360",cells_NSCs_10X]) , gene = "Slc1a3"   ) %>% rownames_to_column("name") %>% left_join(anno_10X_NSCs , by = "name" )
Column `name` joining character vector and factor, coercing into character vector
addmargins( table( Slc1a3_df$age  , Slc1a3_df$geneReads > 0 ) )
       
        FALSE TRUE  Sum
  old      26  995 1021
  young    48 1177 1225
  Sum      74 2172 2246
gg_Slc1a3 <- ggplot(data = Slc1a3_df , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") )
gg_Slc1a3

Sox2 - ENSMUSG00000074637

Sox2_df <- data.frame( totalReads = colSums(data_10X_NSCs) , geneReads = as.numeric(data_10X_NSCs["ENSMUSG00000074637",cells_NSCs_10X]) , gene = "Sox2"   ) %>% rownames_to_column("name") %>% left_join(anno_10X_NSCs , by = "name" )
Column `name` joining character vector and factor, coercing into character vector
addmargins( table( Sox2_df$age  , Sox2_df$geneReads > 0 ) )
       
        FALSE TRUE  Sum
  old     209  812 1021
  young   202 1023 1225
  Sum     411 1835 2246
gg_Sox2 <- ggplot(data = Sox2_df , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") )
gg_Sox2

interferon_genes_bulk_df <- bind_rows( Ifit1_df , C3_df , Rsad2_df , Iigp1_df , Ifi47_df , Il33_df , Cxcl10_df , Sox2_df , Slc1a3_df )
Unequal factor levels: coercing to characterbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vector

Combine all genes

Scatterplot

gg_interferon_genes <- ggplot(data = interferon_genes_bulk_df , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") ) + facet_grid( gene ~ . )
gg_interferon_genes

Barplot

Show the same data as stacked barplots

interferon_genes_bulk_df_bars <- interferon_genes_bulk_df %>% mutate( interval = base::cut( geneReads , breaks = c(-0.1, 0.5,5.5,50.5,Inf) , labels = c("0","1-5","5-50","50-") ) ) %>% group_by(interval,gene,age) %>% summarise( counts = n() ) %>% ungroup() %>% mutate( interval = fct_rev( interval ) , age = fct_recode( .f = age ,  "22 month" = "old",  "2 month" = "young" ) %>% fct_rev() ) %>% group_by(gene,age) %>% mutate( Percent = 100*counts/sum(counts) )
ggplot( data = interferon_genes_bulk_df_bars , mapping = aes(x = age , y = Percent , fill = interval) ) + geom_bar(stat = "identity") + facet_grid(facets = . ~ gene ) + viridis::scale_fill_viridis( discrete = TRUE , direction = -1 ) + ggtitle("Read counts: 10X")

Bulk NSC genes in InnateDB

Which genes are the ones with high fold changes in the bulk and are annotated in InnateDB

innateDB_genes_NSCs_bulk_up_table <- structure(list(ensembl_gene_id = structure(c(24L, 25L, 9L, 40L, 
34L, 15L, 17L, 31L, 22L, 30L, 26L, 20L, 19L, 33L, 27L, 4L, 16L, 
8L, 7L, 29L, 35L, 1L, 12L, 2L, 32L, 13L, 36L, 10L, 3L, 28L, 21L, 
39L, 23L, 11L, 14L, 6L, 5L, 18L, 38L, 37L), .Label = c("ENSMUSG00000000386", 
"ENSMUSG00000002325", "ENSMUSG00000005413", "ENSMUSG00000014361", 
"ENSMUSG00000015766", "ENSMUSG00000017716", "ENSMUSG00000018899", 
"ENSMUSG00000020122", "ENSMUSG00000020641", "ENSMUSG00000021508", 
"ENSMUSG00000022306", "ENSMUSG00000023951", "ENSMUSG00000024066", 
"ENSMUSG00000024079", "ENSMUSG00000024164", "ENSMUSG00000025492", 
"ENSMUSG00000025498", "ENSMUSG00000026104", "ENSMUSG00000026896", 
"ENSMUSG00000028270", "ENSMUSG00000029167", "ENSMUSG00000029826", 
"ENSMUSG00000030341", "ENSMUSG00000034459", "ENSMUSG00000034855", 
"ENSMUSG00000035692", "ENSMUSG00000036905", "ENSMUSG00000036913", 
"ENSMUSG00000037860", "ENSMUSG00000038418", "ENSMUSG00000038642", 
"ENSMUSG00000040747", "ENSMUSG00000046688", "ENSMUSG00000046718", 
"ENSMUSG00000053113", "ENSMUSG00000054717", "ENSMUSG00000060441", 
"ENSMUSG00000064215", "ENSMUSG00000074151", "ENSMUSG00000074896"
), class = "factor"), baseMean = c(1424.95976205735, 146.693384980886, 
254.747257624993, 1378.74240836277, 232.679222864689, 53.3856147003376, 
127.295243864467, 100.858827865452, 1254.48226869821, 8484.55808366433, 
133.391584778487, 449.698563109268, 435.970338794981, 487.652611748752, 
38.1324871848669, 2270.44585158928, 392.455793416614, 8123.4459446361, 
495.513923324155, 147.634742373719, 285.437837120322, 20.0485104704833, 
243.691996897216, 513.924995107202, 17.4190622356759, 18.1436595206331, 
15890.0096695135, 1952.66573537053, 149.127545170108, 284.046380761723, 
1257.30518127357, 52.8851930781209, 761.966603230871, 315.283958297394, 
633.932254776546, 1091.87572537937, 450.61462668542, 1757.25022917456, 
2425.56654256373, 64.6090952815893), log2FoldChange = c(4.55242859938374, 
6.95678411131362, 5.03641775134329, 3.37966257532159, 3.73428610778296, 
5.53141829606081, 3.86121962444797, 3.75531459127234, 2.42356052609584, 
2.16374238670928, 3.1971506351276, 2.44063114014779, 2.44677858670807, 
2.0893762261114, 3.67190586863129, 1.88755481055364, 2.10038800171168, 
1.69845072572928, 1.91183744819457, 2.41403420088619, 2.0284929496982, 
3.7014584495119, 1.94105240967367, 1.98333192338338, 3.4448344969081, 
3.44363682239329, 1.57281037684989, 1.63883482734508, 2.11717369111306, 
1.8668146256893, 1.43386187193389, 2.76038968668686, 1.7580616439951, 
1.52810896028218, 1.55394451633379, 1.28056799567681, 1.42242513375296, 
1.26735536508083, 1.18570808766668, 2.12810319958397), lfcSE = c(0.519384243203232, 
0.81388488255093, 0.641951444263734, 0.48612175786269, 0.578514935508735, 
0.878589568240684, 0.670578673894941, 0.684535958284751, 0.448804291965827, 
0.41379046101385, 0.645816741770005, 0.504575112575078, 0.531966043635904, 
0.460946382208802, 0.845572909706191, 0.436183111157877, 0.505011402058278, 
0.415807705611759, 0.488183380722589, 0.618808403224005, 0.520676205372646, 
0.965738215947367, 0.521153307010798, 0.553367834874403, 0.973891191367301, 
0.97448322263972, 0.448895240692065, 0.468806743609123, 0.606652875776758, 
0.541455272040843, 0.42118712809647, 0.818497304894856, 0.535426580260121, 
0.488159657804439, 0.500868295142623, 0.422276770181993, 0.470630833485906, 
0.427622347529472, 0.406142195700318, 0.7298433146452), stat = c(8.76504949651004, 
8.54762664900375, 7.84548083246336, 6.95229645795079, 6.45495194432465, 
6.29579327596285, 5.75804118854651, 5.48592743130992, 5.40003865711778, 
5.2290774934922, 4.95055397041132, 4.83700261729543, 4.59950144558989, 
4.53279666953747, 4.34250651420131, 4.32743671698567, 4.1590902564796, 
4.08470238239196, 3.91622804808462, 3.90110119434225, 3.89588179518673, 
3.83277619999832, 3.7245324620639, 3.58411132413133, 3.53718621489091, 
3.53380821997635, 3.50373591492099, 3.49575779292008, 3.48992607741657, 
3.44777255312879, 3.40433450189738, 3.37250919481214, 3.28347846149326, 
3.13034667214217, 3.10250125912102, 3.03253241973247, 3.02237981990518, 
2.96372575568792, 2.91944077768661, 2.91583571005033), pvalue = c(1.86694618820708e-18, 
1.25645385522089e-17, 4.31297720037574e-15, 3.59387157612209e-12, 
1.08253248479473e-10, 3.05831732678035e-10, 8.50955946071268e-09, 
4.11305732780317e-08, 6.66265384184588e-08, 1.70357951680874e-07, 
7.40025308679816e-07, 1.31811693167268e-06, 4.23503254113743e-06, 
5.82078401153992e-06, 1.40866307399552e-05, 1.50854667946568e-05, 
3.19517635376871e-05, 4.41333464318532e-05, 8.99451714694149e-05, 
9.57561097095347e-05, 9.78421013854258e-05, 0.000126705214745366, 
0.000195677532925451, 0.000338227814291896, 0.000404414420179005, 
0.000409618180066453, 0.0004587800815838, 0.000472717577863198, 
0.000483154175333289, 0.000565229800758029, 0.000663254779320521, 
0.000744865993665538, 0.00102534479496883, 0.00174600119776919, 
0.00191892731640661, 0.00242511046065225, 0.00250795646620197, 
0.00303939026229686, 0.00350660015484411, 0.00354737241964209
), padj = c(2.76849450249227e-15, 1.43322724762081e-14, 3.55317438357621e-12, 
1.15855481744162e-09, 2.43225366924561e-08, 5.96734047879288e-08, 
1.10691453721849e-06, 4.09345819557002e-06, 6.02442035492272e-06, 
1.40346559193094e-05, 5.05706695963733e-05, 7.72583240307279e-05, 
0.00020864218455989, 0.000272291501915222, 0.000555143310344139, 
0.00058560834318839, 0.00109173433525429, 0.00143520481192533, 
0.00261017015209384, 0.00274124971212874, 0.00278483785306042, 
0.00341326958812737, 0.00486047258919852, 0.007542225952082, 
0.00869139338671661, 0.00873989639166249, 0.00959555688266032, 
0.00981782767805792, 0.00993716125661212, 0.0113267469127579, 
0.0128318926960938, 0.0140529488804914, 0.0180028284113852, 0.0273477266839752, 
0.0294878478497343, 0.034989937779684, 0.0357257314479433, 0.0415793911120156, 
0.0457740965635417, 0.0462249434190444), gene_symbol = c("Ifit1", 
"Cxcl10", "Rsad2", "Ifit3", "Bst2", "C3", "Irf7", "Ctss", "Zc3hav1", 
"Egr1", "Isg15", "Gbp2", "Ifih1", "Tifa", "C1qb", "Mertk", "Ifitm3", 
"Egfr", "Irf1", "Aim2", "Socs3", "Mx1", "Vegfa", "Irf9", "Cd53", 
"Xdh", "Hmgb2", "Cxcl14", "Hmox1", "Trim67", "Ppargc1a", "Nlrc5", 
"Tnfrsf1a", "Zfpm2", "Eif2ak2", "Birc5", "Eps8", "Stat1", "Ifi27", 
"Trim5"), entrezgene = c(15957L, 15945L, 58185L, 15959L, 69550L, 
102642251L, 54123L, 13040L, 78781L, 13653L, 100038882L, 14469L, 
71586L, 211550L, 12260L, 17289L, 66141L, 13649L, 16362L, 383619L, 
12702L, NA, 22339L, 16391L, 12508L, 22436L, 97165L, 57266L, 15368L, 
330863L, 19017L, 434341L, 21937L, 22762L, 19106L, 11799L, 13860L, 
20846L, 52668L, 667823L), description = structure(c(19L, 6L, 
30L, 20L, 3L, 10L, 24L, 4L, 39L, 11L, 26L, 15L, 22L, 33L, 9L, 
8L, 21L, 13L, 23L, 1L, 32L, 27L, 37L, 25L, 5L, 38L, 17L, 7L, 
16L, 35L, 29L, 28L, 36L, 40L, 14L, 2L, 12L, 31L, 18L, 34L), .Label = c("absent in melanoma 2 [Source:MGI Symbol;Acc:MGI:2686159]", 
"baculoviral IAP repeat-containing 5 [Source:MGI Symbol;Acc:MGI:1203517]", 
"bone marrow stromal cell antigen 2 [Source:MGI Symbol;Acc:MGI:1916800]", 
"cathepsin S [Source:MGI Symbol;Acc:MGI:107341]", "CD53 antigen [Source:MGI Symbol;Acc:MGI:88341]", 
"chemokine (C-X-C motif) ligand 10 [Source:MGI Symbol;Acc:MGI:1352450]", 
"chemokine (C-X-C motif) ligand 14 [Source:MGI Symbol;Acc:MGI:1888514]", 
"c-mer proto-oncogene tyrosine kinase [Source:MGI Symbol;Acc:MGI:96965]", 
"complement component 1, q subcomponent, beta polypeptide [Source:MGI Symbol;Acc:MGI:88224]", 
"complement component 3 [Source:MGI Symbol;Acc:MGI:88227]", "early growth response 1 [Source:MGI Symbol;Acc:MGI:95295]", 
"epidermal growth factor receptor pathway substrate 8 [Source:MGI Symbol;Acc:MGI:104684]", 
"epidermal growth factor receptor [Source:MGI Symbol;Acc:MGI:95294]", 
"eukaryotic translation initiation factor 2-alpha kinase 2 [Source:MGI Symbol;Acc:MGI:1353449]", 
"guanylate binding protein 2 [Source:MGI Symbol;Acc:MGI:102772]", 
"heme oxygenase (decycling) 1 [Source:MGI Symbol;Acc:MGI:96163]", 
"high mobility group box 2 [Source:MGI Symbol;Acc:MGI:96157]", 
"interferon, alpha-inducible protein 27 [Source:MGI Symbol;Acc:MGI:1277180]", 
"interferon-induced protein with tetratricopeptide repeats 1 [Source:MGI Symbol;Acc:MGI:99450]", 
"interferon-induced protein with tetratricopeptide repeats 3 [Source:MGI Symbol;Acc:MGI:1101055]", 
"interferon induced transmembrane protein 3 [Source:MGI Symbol;Acc:MGI:1913391]", 
"interferon induced with helicase C domain 1 [Source:MGI Symbol;Acc:MGI:1918836]", 
"interferon regulatory factor 1 [Source:MGI Symbol;Acc:MGI:96590]", 
"interferon regulatory factor 7 [Source:MGI Symbol;Acc:MGI:1859212]", 
"interferon regulatory factor 9 [Source:MGI Symbol;Acc:MGI:107587]", 
"ISG15 ubiquitin-like modifier [Source:MGI Symbol;Acc:MGI:1855694]", 
"myxovirus (influenza virus) resistance 1 [Source:MGI Symbol;Acc:MGI:97243]", 
"NLR family, CARD domain containing 5 [Source:MGI Symbol;Acc:MGI:3612191]", 
"peroxisome proliferative activated receptor, gamma, coactivator 1 alpha [Source:MGI Symbol;Acc:MGI:1342774]", 
"radical S-adenosyl methionine domain containing 2 [Source:MGI Symbol;Acc:MGI:1929628]", 
"signal transducer and activator of transcription 1 [Source:MGI Symbol;Acc:MGI:103063]", 
"suppressor of cytokine signaling 3 [Source:MGI Symbol;Acc:MGI:1201791]", 
"TRAF-interacting protein with forkhead-associated domain [Source:MGI Symbol;Acc:MGI:2182965]", 
"tripartite motif-containing 5 [Source:MGI Symbol;Acc:MGI:3646853]", 
"tripartite motif-containing 67 [Source:MGI Symbol;Acc:MGI:3045323]", 
"tumor necrosis factor receptor superfamily, member 1a [Source:MGI Symbol;Acc:MGI:1314884]", 
"vascular endothelial growth factor A [Source:MGI Symbol;Acc:MGI:103178]", 
"xanthine dehydrogenase [Source:MGI Symbol;Acc:MGI:98973]", "zinc finger CCCH type, antiviral 1 [Source:MGI Symbol;Acc:MGI:1926031]", 
"zinc finger protein, multitype 2 [Source:MGI Symbol;Acc:MGI:1334444]"
), class = "factor"), gene_biotype = structure(c(1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L), .Label = "protein_coding", class = "factor")), row.names = c(NA, 
-40L), .Names = c("ensembl_gene_id", "baseMean", "log2FoldChange", 
"lfcSE", "stat", "pvalue", "padj", "gene_symbol", "entrezgene", 
"description", "gene_biotype"), class = "data.frame")
innateDB_bulk_up_genes <- innateDB_genes_NSCs_bulk_up_table %>% dplyr::arrange( desc( log2FoldChange )) %>% ungroup() %>% dplyr::select(ensembl_gene_id , gene_symbol )
innateDB_bulk_up_genes <- innateDB_bulk_up_genes[1:7,]

We split the df into a list , rows become fields

genes_up_InnateDB_sms2_bulk_list <- split(innateDB_bulk_up_genes , f = seq(nrow(innateDB_bulk_up_genes)))

Next up we calculate the totalReads and reads for only the specified gene, add the information for cells which age and celltype they belong to.

genes_up_InnateDB_data_list <- lapply(genes_up_InnateDB_sms2_bulk_list, function(x){
  raw_count_data <- data.frame( totalReads = colSums(data_10X_NSCs[,as.character(cells_NSCs_10X)]) , geneReads = as.numeric(data_10X_NSCs[ as.character(x$ensembl_gene_id) ,as.character(cells_NSCs_10X)]) , gene = as.character(x$gene_symbol) )  %>% rownames_to_column("name") %>% left_join(anno_10X_NSCs , by = "name" )
  table_cells_expr <- addmargins( table( raw_count_data$age  , raw_count_data$geneReads > 0 ) )
  # fig.width=6 , fig.height=2
  gg <- ggplot(data = raw_count_data , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") ) + ggtitle(x$gene_symbol)
list( "raw_count_data" = raw_count_data , "numbers_cells_expressing" = table_cells_expr , "plot" = gg  )
})

We extract the raw_counts data table from the list we have just created

genes_up_InnateDB_data_list_raw_counts <- lapply(genes_up_InnateDB_data_list, function(x){ x$raw_count_data })

and bind together all the data frames for the different genes

genes_up_InnateDB_data_plot_10X <- bind_rows(genes_up_InnateDB_data_list_raw_counts)
Scatterplot
gg_InnateDB_10X <- ggplot(data = genes_up_InnateDB_data_plot_10X , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") ) + facet_grid( gene ~ .  )
gg_InnateDB_10X

Barplot

Show the same data as stacked barplots

genes_up_InnateDB_data_plot_10X_bars <- genes_up_InnateDB_data_plot_10X %>% mutate( interval = base::cut( geneReads , breaks = c(-0.1, 0.5,5.5,50.5,Inf) , labels = c("0","1-5","5-50","50-") ) ) %>% group_by(interval,gene,age) %>% summarise( counts = n() ) %>% ungroup() %>% mutate( interval = fct_rev( interval ) , age = fct_recode( .f = age ,  "22 month" = "old",  "2 month" = "young" ) %>% fct_rev() ) %>% group_by(gene,age) %>% mutate( Percent = 100*counts/sum(counts) )
ggplot( data = genes_up_InnateDB_data_plot_10X_bars , mapping = aes(x = age , y = Percent , fill = interval) ) + geom_bar(stat = "identity") + facet_grid(facets = . ~ gene ) + viridis::scale_fill_viridis( discrete = TRUE , direction = -1 , drop=FALSE ,  name = "Read count" ) + ggtitle("Read counts: 10X") + theme( axis.text.x = element_text(angle = 90,hjust = 0,vjust = 0.5) , strip.text.x = element_text(size = 8)) 

SMARTseq2

How many counts do genes have in the Single cell data sets, that are significantly upregulated in the bulk NSCs

cells_NSCs_SMARTseq2 <- as.character(anno_SMARTseq2_NSCs$name)

Ifit1 - ENSMUSG00000078920

Ifit1_df_SMARTseq2 <- data.frame( totalReads = colSums(data_SMARTseq2_NSCs) , geneReads = as.numeric(data_SMARTseq2_NSCs["ENSMUSG00000078920",cells_NSCs_SMARTseq2]) , gene = "Ifit1" ) %>% rownames_to_column("name") %>% left_join(anno_SMARTseq2_NSCs , by = "name" )
Column `name` joining character vector and factor, coercing into character vector
addmargins( table( Ifit1_df_SMARTseq2$age  , Ifit1_df_SMARTseq2$geneReads > 0 ) )
       
        FALSE TRUE Sum
  old     132    1 133
  young    89    0  89
  Sum     221    1 222
gg_Ifit1_SMARTseq2 <- ggplot(data = Ifit1_df_SMARTseq2 , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") )
gg_Ifit1_SMARTseq2

Rsad2 - ENSMUSG00000020641

Rsad2_df_SMARTseq2 <- data.frame( totalReads = colSums(data_SMARTseq2_NSCs) , geneReads = as.numeric(data_SMARTseq2_NSCs["ENSMUSG00000020641",cells_NSCs_SMARTseq2]) , gene = "Rsad2"  ) %>% rownames_to_column("name") %>% left_join(anno_SMARTseq2_NSCs , by = "name" )
Column `name` joining character vector and factor, coercing into character vector
addmargins( table( Rsad2_df_SMARTseq2$age  , Rsad2_df_SMARTseq2$geneReads > 0 ) )
       
        FALSE TRUE Sum
  old     122   11 133
  young    86    3  89
  Sum     208   14 222
gg_Rsad2_SMARTseq2 <- ggplot(data = Rsad2_df_SMARTseq2 , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") )
gg_Rsad2_SMARTseq2

C3 - ENSMUSG00000024164

C3_df_SMARTseq2 <- data.frame( totalReads = colSums(data_SMARTseq2_NSCs) , geneReads = as.numeric(data_SMARTseq2_NSCs["ENSMUSG00000024164",cells_NSCs_SMARTseq2]) , gene = "C3"   )  %>% rownames_to_column("name") %>% left_join(anno_SMARTseq2_NSCs , by = "name" )
Column `name` joining character vector and factor, coercing into character vector
addmargins( table( C3_df_SMARTseq2$age  , C3_df_SMARTseq2$geneReads > 0 ) )
       
        FALSE TRUE Sum
  old     133    0 133
  young    88    1  89
  Sum     221    1 222
gg_C3_SMARTseq2 <- ggplot(data = C3_df_SMARTseq2 , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") )
gg_C3_SMARTseq2

Iigp1 - ENSMUSG00000054072

Iigp1_df_SMARTseq2 <- data.frame( totalReads = colSums(data_SMARTseq2_NSCs) , geneReads = as.numeric(data_SMARTseq2_NSCs["ENSMUSG00000054072",cells_NSCs_SMARTseq2]) , gene = "Iigp1"   )  %>% rownames_to_column("name") %>% left_join(anno_SMARTseq2_NSCs , by = "name" )
Column `name` joining character vector and factor, coercing into character vector
addmargins( table( Iigp1_df_SMARTseq2$age  , Iigp1_df_SMARTseq2$geneReads > 0 ) )
       
        FALSE TRUE Sum
  old     116   17 133
  young    89    0  89
  Sum     205   17 222
gg_Iigp1_SMARTseq2 <- ggplot(data = Iigp1_df_SMARTseq2 , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") )
gg_Iigp1_SMARTseq2

Ifi44 - ENSMUSG00000028037

Ifi44_df_SMARTseq2 <- data.frame( totalReads = colSums(data_SMARTseq2_NSCs) , geneReads = as.numeric(data_SMARTseq2_NSCs["ENSMUSG00000028037",cells_NSCs_SMARTseq2]) , gene = "Ifi44"   )  %>% rownames_to_column("name") %>% left_join(anno_SMARTseq2_NSCs , by = "name" )
Column `name` joining character vector and factor, coercing into character vector
addmargins( table( Ifi44_df_SMARTseq2$age  , Ifi44_df_SMARTseq2$geneReads > 0 ) )
       
        FALSE TRUE Sum
  old     126    7 133
  young    88    1  89
  Sum     214    8 222
gg_Ifi44_SMARTseq2 <- ggplot(data = Ifi44_df_SMARTseq2 , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") )
gg_Ifi44_SMARTseq2

Cxcl10 - ENSMUSG00000034855

Cxcl10_df_SMARTseq2 <- data.frame( totalReads = colSums(data_SMARTseq2_NSCs) , geneReads = as.numeric(data_SMARTseq2_NSCs["ENSMUSG00000034855",cells_NSCs_SMARTseq2]) , gene = "Cxcl10"   )  %>% rownames_to_column("name") %>% left_join(anno_SMARTseq2_NSCs , by = "name" )
Column `name` joining character vector and factor, coercing into character vector
addmargins( table( Cxcl10_df_SMARTseq2$age  , Cxcl10_df_SMARTseq2$geneReads > 0 ) )
       
        FALSE TRUE Sum
  old     130    3 133
  young    89    0  89
  Sum     219    3 222
gg_Cxcl10_SMARTseq2 <- ggplot(data = Cxcl10_df_SMARTseq2 , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") )
gg_Cxcl10_SMARTseq2

Il33 - ENSMUSG00000024810

Il33_df_SMARTseq2 <- data.frame( totalReads = colSums(data_SMARTseq2_NSCs) , geneReads = as.numeric(data_SMARTseq2_NSCs["ENSMUSG00000024810",cells_NSCs_SMARTseq2]) , gene = "Il33"   )  %>% rownames_to_column("name") %>% left_join(anno_SMARTseq2_NSCs , by = "name" )
Column `name` joining character vector and factor, coercing into character vector
addmargins( table( Il33_df_SMARTseq2$age  , Il33_df_SMARTseq2$geneReads > 0 ) )
       
        FALSE TRUE Sum
  old      58   75 133
  young    54   35  89
  Sum     112  110 222
gg_Il33_SMARTseq2 <- ggplot(data = Il33_df_SMARTseq2 , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") )
gg_Il33_SMARTseq2

Ddx60 - ENSMUSG00000037921

Ddx60_df_SMARTseq2 <- data.frame( totalReads = colSums(data_SMARTseq2_NSCs) , geneReads = as.numeric(data_SMARTseq2_NSCs["ENSMUSG00000037921",cells_NSCs_SMARTseq2]) , gene = "Ddx60"   ) %>% rownames_to_column("name") %>% left_join(anno_SMARTseq2_NSCs , by = "name" )
Column `name` joining character vector and factor, coercing into character vector
addmargins( table( Ddx60_df_SMARTseq2$age  , Ddx60_df_SMARTseq2$geneReads > 0 ) )
       
        FALSE TRUE Sum
  old     113   20 133
  young    87    2  89
  Sum     200   22 222
gg_Ddx60_SMARTseq2 <- ggplot(data = Ddx60_df_SMARTseq2 , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") )
gg_Ddx60_SMARTseq2

Ifi47 - ENSMUSG00000078920

Ifi47_df_SMARTseq2 <- data.frame( totalReads = colSums(data_SMARTseq2_NSCs) , geneReads = as.numeric(data_SMARTseq2_NSCs["ENSMUSG00000078920",cells_NSCs_SMARTseq2]) , gene = "Ifi47"   ) %>% rownames_to_column("name") %>% left_join(anno_SMARTseq2_NSCs , by = "name" )
Column `name` joining character vector and factor, coercing into character vector
addmargins( table( Ifi47_df_SMARTseq2$age  , Ifi47_df_SMARTseq2$geneReads > 0 ) )
       
        FALSE TRUE Sum
  old     132    1 133
  young    89    0  89
  Sum     221    1 222
gg_Ifi47_SMARTseq2 <- ggplot(data = Ifi47_df_SMARTseq2 , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") )
gg_Ifi47_SMARTseq2

Gm4951 - ENSMUSG00000073555

Gm4951_df_SMARTseq2 <- data.frame( totalReads = colSums(data_SMARTseq2_NSCs) , geneReads = as.numeric(data_SMARTseq2_NSCs["ENSMUSG00000073555",cells_NSCs_SMARTseq2]) , gene = "Gm4951"   ) %>% rownames_to_column("name") %>% left_join(anno_SMARTseq2_NSCs , by = "name" )
Column `name` joining character vector and factor, coercing into character vector
addmargins( table( Gm4951_df_SMARTseq2$age  , Gm4951_df_SMARTseq2$geneReads > 0 ) )
       
        FALSE TRUE Sum
  old     120   13 133
  young    89    0  89
  Sum     209   13 222
gg_Gm4951_SMARTseq2 <- ggplot(data = Gm4951_df_SMARTseq2 , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") )
gg_Gm4951_SMARTseq2

Slc1a3 - ENSMUSG00000005360

Slc1a3_df_SMARTseq2 <- data.frame( totalReads = colSums(data_SMARTseq2_NSCs) , geneReads = as.numeric(data_SMARTseq2_NSCs["ENSMUSG00000005360",cells_NSCs_SMARTseq2]) , gene = "Slc1a3"   ) %>% rownames_to_column("name") %>% left_join(anno_SMARTseq2_NSCs , by = "name" )
Column `name` joining character vector and factor, coercing into character vector
addmargins( table( Slc1a3_df_SMARTseq2$age  , Slc1a3_df_SMARTseq2$geneReads > 0 ) )
       
        TRUE Sum
  old    133 133
  young   89  89
  Sum    222 222
gg_Slc1a3_SMARTseq2 <- ggplot(data = Slc1a3_df_SMARTseq2 , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") )
gg_Slc1a3_SMARTseq2

Sox2 - ENSMUSG00000074637

Sox2_df_SMARTseq2 <- data.frame( totalReads = colSums(data_SMARTseq2_NSCs) , geneReads = as.numeric(data_SMARTseq2_NSCs["ENSMUSG00000074637",cells_NSCs_SMARTseq2]) , gene = "Sox2"   ) %>% rownames_to_column("name") %>% left_join(anno_SMARTseq2_NSCs , by = "name" )
Column `name` joining character vector and factor, coercing into character vector
addmargins( table( Sox2_df_SMARTseq2$age  , Sox2_df_SMARTseq2$geneReads > 0 ) )
       
        FALSE TRUE Sum
  old      48   85 133
  young    25   64  89
  Sum      73  149 222
gg_Sox2_SMARTseq2 <- ggplot(data = Sox2_df_SMARTseq2 , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") )
gg_Sox2_SMARTseq2

Combine all genes

interferon_genes_bulk_df_SMARTseq2 <- bind_rows( Ifit1_df_SMARTseq2 , C3_df_SMARTseq2 , Rsad2_df_SMARTseq2 , Iigp1_df_SMARTseq2 , Ifi47_df_SMARTseq2 , Il33_df_SMARTseq2 , Cxcl10_df_SMARTseq2 , Sox2_df_SMARTseq2 , Slc1a3_df_SMARTseq2 )
Unequal factor levels: coercing to characterbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vectorbinding character and factor vector, coercing into character vector

Scatterplot

gg_interferon_genes_SMARTseq2 <- ggplot(data = interferon_genes_bulk_df_SMARTseq2 , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") ) + facet_grid( gene ~ . ) + scale_y_log10() + ylab("log10( geneReads )")
gg_interferon_genes_SMARTseq2

Barplot

Show the same data as stacked barplots

interferon_genes_bulk_df_SMARTseq2_bars <- interferon_genes_bulk_df_SMARTseq2 %>% mutate( interval = base::cut( geneReads , breaks = c(-0.1, 0.5,5.5,50.5,Inf) , labels = c("0","1-5","5-50","50-") ) ) %>% group_by(interval,gene,age) %>% summarise( counts = n() ) %>% ungroup() %>% mutate( interval = fct_rev( interval ) , age = fct_recode( .f = age ,  "22 month" = "old",  "2 month" = "young" ) %>% fct_rev() ) %>% group_by(gene,age) %>% mutate( Percent = 100*counts/sum(counts) )
ggplot( data = interferon_genes_bulk_df_SMARTseq2_bars , mapping = aes(x = age , y = Percent , fill = interval) ) + geom_bar(stat = "identity") + facet_grid(facets = . ~ gene ) + viridis::scale_fill_viridis( discrete = TRUE , direction = -1 ) + ggtitle("Read counts: SMARTseq2")

Bulk NSC genes in InnateDB

Which genes are the ones with high fold changes in the bulk and are annotated in InnateDB

innateDB_bulk_up_genes <- innateDB_genes_NSCs_bulk_up_table %>% dplyr::arrange( desc( log2FoldChange )) %>% ungroup() %>% dplyr::select(ensembl_gene_id , gene_symbol )
innateDB_bulk_up_genes <- innateDB_bulk_up_genes[1:7,]

We split the df into a list , rows become fields

genes_up_InnateDB_sms2_bulk_list <- split(innateDB_bulk_up_genes , f = seq(nrow(innateDB_bulk_up_genes)))

Next up we calculate the totalReads and reads for only the specified gene, add the information for cells which age and celltype they belong to.

genes_up_InnateDB_data_list_SMARTseq2 <- lapply(genes_up_InnateDB_sms2_bulk_list, function(x){
  raw_count_data <- data.frame( totalReads = colSums(data_SMARTseq2_NSCs[,as.character(cells_NSCs_SMARTseq2)]) , geneReads = as.numeric(data_SMARTseq2_NSCs[ as.character(x$ensembl_gene_id) ,as.character(cells_NSCs_SMARTseq2) ]) , gene = as.character( x$gene_symbol ) )  %>% rownames_to_column("name") %>% left_join(anno_SMARTseq2_NSCs , by = "name" )
  table_cells_expr <- addmargins( table( raw_count_data$age  , raw_count_data$geneReads > 0 ) )
  # fig.width=6 , fig.height=2
  gg <- ggplot(data = raw_count_data , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") ) + ggtitle(x$gene_symbol)
list( "raw_count_data" = raw_count_data , "numbers_cells_expressing" = table_cells_expr , "plot" = gg  )
})

We extract the raw_counts data table from the list we have just created

genes_up_InnateDB_data_list_SMARTseq2_raw_counts <- lapply(genes_up_InnateDB_data_list_SMARTseq2, function(x){ x$raw_count_data })

and bind together all the data frames for the different genes

genes_up_InnateDB_data_plot_SMARTseq2 <- bind_rows(genes_up_InnateDB_data_list_SMARTseq2_raw_counts)
Scatterplot
gg_InnateDB_SMARTseq2 <- ggplot(data = genes_up_InnateDB_data_plot_SMARTseq2 , mapping = aes(x = totalReads , y = geneReads ) ) + geom_point(aes(color = age)) + scale_color_manual(values = c("old" = "slateblue" , "young" = "yellowgreen") ) + facet_grid( gene ~ .  )
gg_InnateDB_SMARTseq2

Barplot

Show the same data as stacked barplots

genes_up_InnateDB_data_plot_SMARTseq2_bars <- genes_up_InnateDB_data_plot_SMARTseq2 %>% mutate( interval = base::cut( geneReads , breaks = c(-0.1, 0.5,5.5,50.5,Inf) , labels = c("0","1-5","5-50","50-") ) ) %>% group_by(interval,gene,age) %>% summarise( counts = n() ) %>% ungroup() %>% mutate( interval = fct_rev( interval ) , age = fct_recode( .f = age ,  "22 month" = "old",  "2 month" = "young" ) %>% fct_rev() ) %>% group_by(gene,age) %>% mutate( Percent = 100*counts/sum(counts) )
ggplot( data = genes_up_InnateDB_data_plot_SMARTseq2_bars , mapping = aes(x = age , y = Percent , fill = interval) ) + geom_bar(stat = "identity") + facet_grid(facets = . ~ gene ) + viridis::scale_fill_viridis( discrete = TRUE , drop=FALSE , direction = -1 , name = "Read count" ) + ggtitle("Read counts: SMARTseq2") + theme( axis.text.x = element_text(angle = 90,hjust = 0,vjust = 0.5))

Combined Barplots for 10X and SMARTseq2

genes_up_InnateDB_data_barplot <- bind_rows(
  dplyr::mutate( genes_up_InnateDB_data_plot_SMARTseq2_bars , seqmethod = "Smart-Seq2" ) , 
  dplyr::mutate( genes_up_InnateDB_data_plot_10X_bars  , seqmethod = "10X" ) 
)
idx = 1
gg1 <- ggplot( data = genes_up_InnateDB_data_barplot %>% dplyr::filter( gene == innateDB_bulk_up_genes$gene_symbol[[idx]] ) , mapping = aes(x = age , y = Percent , fill = interval) ) + geom_bar(stat = "identity") + facet_grid(facets = . ~ seqmethod ) + viridis::scale_fill_viridis( discrete = TRUE , drop=FALSE , direction = -1 , name = "Read count" ) + ggtitle( paste("Read counts:", innateDB_bulk_up_genes$gene_symbol[[idx]] ) ) + theme( axis.text.x = element_text(angle = 90,hjust = 0,vjust = 0.5))
gg1

idx = 2
 gg2 <- ggplot( data = genes_up_InnateDB_data_barplot %>% dplyr::filter( gene == innateDB_bulk_up_genes$gene_symbol[idx] ) , mapping = aes(x = age , y = Percent , fill = interval) ) + geom_bar(stat = "identity") + facet_grid(facets = . ~ seqmethod ) + viridis::scale_fill_viridis( discrete = TRUE , drop=FALSE , direction = -1 , name = "Read count" ) + ggtitle( paste("Read counts:", innateDB_bulk_up_genes$gene_symbol[[idx]] ) ) + theme( axis.text.x = element_text(angle = 90,hjust = 0,vjust = 0.5))
 
 gg2

idx = 3
gg3 <- ggplot( data = genes_up_InnateDB_data_barplot %>% dplyr::filter( gene == innateDB_bulk_up_genes$gene_symbol[idx] ) , mapping = aes(x = age , y = Percent , fill = interval) ) + geom_bar(stat = "identity") + facet_grid(facets = . ~ seqmethod ) + viridis::scale_fill_viridis( discrete = TRUE , drop=FALSE , direction = -1 , name = "Read count" ) + ggtitle( paste("Read counts:", innateDB_bulk_up_genes$gene_symbol[[idx]] ) ) + theme( axis.text.x = element_text(angle = 90,hjust = 0,vjust = 0.5))
gg3

idx = 4
gg4 <- ggplot( data = genes_up_InnateDB_data_barplot %>% dplyr::filter( gene == innateDB_bulk_up_genes$gene_symbol[idx] ) , mapping = aes(x = age , y = Percent , fill = interval) ) + geom_bar(stat = "identity") + facet_grid(facets = . ~ seqmethod ) + viridis::scale_fill_viridis( discrete = TRUE , drop=FALSE , direction = -1 , name = "Read count" ) + ggtitle( paste("Read counts:", innateDB_bulk_up_genes$gene_symbol[[idx]] ) ) + theme( axis.text.x = element_text(angle = 90,hjust = 0,vjust = 0.5))
gg4

idx = 5
gg5 <- ggplot( data = genes_up_InnateDB_data_barplot %>% dplyr::filter( gene == innateDB_bulk_up_genes$gene_symbol[idx] ) , mapping = aes(x = age , y = Percent , fill = interval) ) + geom_bar(stat = "identity") + facet_grid(facets = . ~ seqmethod ) + viridis::scale_fill_viridis( discrete = TRUE , drop=FALSE , direction = -1 , name = "Read count" ) + ggtitle( paste("Read counts:", innateDB_bulk_up_genes$gene_symbol[[idx]] ) ) + theme( axis.text.x = element_text(angle = 90,hjust = 0,vjust = 0.5))
gg5

idx = 6
gg6 <- ggplot( data = genes_up_InnateDB_data_barplot %>% dplyr::filter( gene == innateDB_bulk_up_genes$gene_symbol[idx] ) , mapping = aes(x = age , y = Percent , fill = interval) ) + geom_bar(stat = "identity") + facet_grid(facets = . ~ seqmethod ) + viridis::scale_fill_viridis( discrete = TRUE , drop=FALSE , direction = -1 , name = "Read count" ) + ggtitle( paste("Read counts:", innateDB_bulk_up_genes$gene_symbol[[idx]] ) ) + theme( axis.text.x = element_text(angle = 90,hjust = 0,vjust = 0.5))
gg6

idx = 7
gg7 <- ggplot( data = genes_up_InnateDB_data_barplot %>% dplyr::filter( gene == innateDB_bulk_up_genes$gene_symbol[idx] ) , mapping = aes(x = age , y = Percent , fill = interval) ) + geom_bar(stat = "identity") + facet_grid(facets = . ~ seqmethod ) + viridis::scale_fill_viridis( discrete = TRUE , drop=FALSE , direction = -1 , name = "Read count" ) + ggtitle( paste("Read counts:", innateDB_bulk_up_genes$gene_symbol[[idx]] ) ) + theme( axis.text.x = element_text(angle = 90,hjust = 0,vjust = 0.5))
gg7

gridExtra::grid.arrange(gg1, gg2, gg3 , gg4, gg5, gg6 , gg7 , nrow = 1)

SessionInfo

sessionInfo()
R version 3.4.3 (2017-11-30)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Ubuntu 16.04.3 LTS

Matrix products: default
BLAS: /usr/lib/libblas/libblas.so.3.6.0
LAPACK: /usr/lib/lapack/liblapack.so.3.6.0

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8    LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C             LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] parallel  stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] biomaRt_2.34.2      ggbeeswarm_0.6.0    viridisLite_0.3.0   bindrcpp_0.2        forcats_0.3.0       dplyr_0.7.4        
 [7] ggrepel_0.8.0       Seurat_2.2.0        Biobase_2.38.0      BiocGenerics_0.24.0 Matrix_1.2-14       cowplot_0.7.0      
[13] ggplot2_2.2.1       pheatmap_1.0.10     tibble_1.4.2        stringr_1.3.1       tidyr_0.7.2        

loaded via a namespace (and not attached):
  [1] readxl_1.1.0         backports_1.1.2      Hmisc_4.1-1          VGAM_1.0-3           NMF_0.20.6          
  [6] sn_1.5-0             plyr_1.8.4           igraph_1.0.1         lazyeval_0.2.0       splines_3.4.3       
 [11] gridBase_0.4-7       digest_0.6.12        foreach_1.4.3        htmltools_0.3.6      viridis_0.4.1       
 [16] lars_1.2             gdata_2.17.0         memoise_1.0.0        magrittr_1.5         checkmate_1.8.5     
 [21] cluster_2.0.6        doParallel_1.0.10    mixtools_1.0.4       ROCR_1.0-7           openxlsx_4.1.0      
 [26] R.utils_2.6.0        prettyunits_1.0.2    colorspace_1.3-2     blob_1.1.1           haven_1.1.2         
 [31] RCurl_1.95-4.10      crayon_1.3.4         jsonlite_1.5         lme4_1.1-18-1        bindr_0.1           
 [36] survival_2.41-3      iterators_1.0.8      ape_5.1              glue_1.3.0           registry_0.3        
 [41] gtable_0.2.0         MatrixModels_0.4-1   car_3.0-2            kernlab_0.9-25       prabclus_2.2-6      
 [46] DEoptimR_1.0-8       abind_1.4-5          scales_0.5.0         mvtnorm_1.0-5        DBI_1.0.0           
 [51] rngtools_1.2.4       Rcpp_0.12.18         dtw_1.20-1           progress_1.2.0       xtable_1.8-2        
 [56] htmlTable_1.12       tclust_1.2-3         bit_1.1-14           foreign_0.8-69       proxy_0.4-22        
 [61] mclust_5.2.2         SDMTools_1.1-221     Formula_1.2-3        tsne_0.1-3           stats4_3.4.3        
 [66] httr_1.3.1           htmlwidgets_1.2      FNN_1.1              gplots_3.0.1         RColorBrewer_1.1-2  
 [71] fpc_2.1-10           acepack_1.4.1        modeltools_0.2-22    ica_1.0-2            XML_3.98-1.11       
 [76] pkgconfig_2.0.2      R.methodsS3_1.7.1    flexmix_2.3-13       nnet_7.3-12          caret_6.0-73        
 [81] AnnotationDbi_1.40.0 labeling_0.3         rlang_0.2.2          reshape2_1.4.2       munsell_0.4.3       
 [86] cellranger_1.1.0     tools_3.4.3          RSQLite_2.1.1        ranger_0.6.0         ggridges_0.5.0      
 [91] evaluate_0.11        yaml_2.2.0           bit64_0.9-7          ModelMetrics_1.1.0   knitr_1.20          
 [96] zip_1.0.0            robustbase_0.92-7    caTools_1.17.1       purrr_0.2.4          pbapply_1.3-1       
[101] nlme_3.1-131         R.oo_1.22.0          compiler_3.4.3       rstudioapi_0.7       beeswarm_0.2.3      
[106] curl_3.2             stringi_1.2.4        lattice_0.20-35      trimcluster_0.1-2    nloptr_1.0.4        
[111] diffusionMap_1.1-0.1 pillar_1.3.0         data.table_1.11.6    bitops_1.0-6         irlba_2.1.2         
[116] R6_2.2.2             latticeExtra_0.6-28  KernSmooth_2.23-15   gridExtra_2.2.1      rio_0.5.10          
[121] IRanges_2.12.0       vipor_0.4.5          codetools_0.2-15     boot_1.3-20          MASS_7.3-48         
[126] gtools_3.5.0         assertthat_0.2.0     pkgmaker_0.22        rprojroot_1.3-2      mnormt_1.5-5        
[131] S4Vectors_0.16.0     diptest_0.75-7       hms_0.4.2            grid_3.4.3           rpart_4.1-12        
[136] minqa_1.2.4          class_7.3-14         rmarkdown_1.10       segmented_0.5-1.4    carData_3.0-1       
[141] Rtsne_0.11           numDeriv_2016.8-1    scatterplot3d_0.3-41 base64enc_0.1-3     
LS0tCnRpdGxlOiAiQ29tcGFyaXNvbiBvZiBidWxrIHNlcXVlbmNpbmcsIFNNQVJUc2VxMiBkYXRhIGFuZCAxMFggc2luZ2xlIGNlbGwgZGF0YSIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOiAKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRvY19jb2xsYXBzZWQ6IHRydWUKICB0b2NfZGVwdGg6IDMKLS0tCgojIExvYWQgdGhlIHBhY2thZ2VzIHVzZWQKCmBgYHtyfQpsaWJyYXJ5KCJ0aWR5ciIpCmxpYnJhcnkoInN0cmluZ3IiKQpsaWJyYXJ5KCJ0aWJibGUiKQpsaWJyYXJ5KCJwaGVhdG1hcCIpCmxpYnJhcnkoIlNldXJhdCIpCmxpYnJhcnkoImdncmVwZWwiKQpsaWJyYXJ5KCJkcGx5ciIpCmxpYnJhcnkoImZvcmNhdHMiKQpgYGAKCiMgTG9hZCBhbGwgdGhlIGRhdGEKCldlIGhhdmUgdGhyZWUgZGlmZmVyZW50IHNlcXVlbmNpbmcgZXhwZXJpbWVudHMgYW5kIHdhbnQgdG8gY3Jvc3MgY29tcGFyZSBiZXR3ZWVuCgotIEJ1bGsgc2VxdWVuY2luZyBvZiBOU0NzLCBOZXVyb2JsYXN0cywgTWljcm9nbGlhIGFuZCBFbmRvdGhlbGlhbCBDZWxscwotIFNpbmdsZSBDZWxsIFJOQSBzZXF1ZW5jaW5nIG9mIE5ldXJhbCBTdGVtIGNlbGxzIHdpdGgKICAtIFNNQVJUc2VxMiBwcm90b2NvbDogcU5TQzEgLSBhTlNDMgogIC0gMTBYIHByb3RvY29sOiBxTlNDMSAtIE5CICsgT1BDICYgT0QKCgpEZWZpbmUgY29sb3JzIGZvciB5b3VuZyBhbmQgb2xkIHRocm91Z2hvdXQgdGhlIGFuYWx5c2lzCgpgYGB7cn0KYWdlX2NvbG9ycyA8LSBjKHlvdW5nID0gInllbGxvd2dyZWVuIiAsIG9sZCA9ICJzbGF0ZWJsdWUiKQphZ2VfY29sb3JzX2J1bGsgPC0gYyh5b3VuZyA9ICJ5ZWxsb3dncmVlbiIgLCBvbGQgPSAic2xhdGVibHVlIiAsIE1vNyA9ICJzbGF0ZWJsdWUxIikKYGBgCgoKIyMgTG9hZCB0aGUgYnVsayBzZXF1ZW5jaW5nIFRQTSB2YWx1ZXMKCmBgYHtyfQpidWxrX2RhdGEgPC0gcmVhZC5jc3YoZmlsZSA9ICIuLi9CdWxrX3NlcXVlbmNpbmcvY291bnRfdGFibGUvR2VuZV9leHByZXNzaW9uX21hdHJpeF9wb3B1bGF0aW9uLmNzdiIgLCByb3cubmFtZXMgPSAxKQoKYnVsa19kYXRhIDwtIGFzLm1hdHJpeChidWxrX2RhdGEpCmBgYAoKTG9nMiB0aGUgVFBNIHZhbHVlcwoKYGBge3J9CmJ1bGtfZGF0YSA8LSBsb2cyKGJ1bGtfZGF0YSsxKQpgYGAKCgpBbHNvIHdlIGxvYWQgdGhlIGFubm90YXRpb24KCmBgYHtyfQpidWxrX2Fubm90YXRpb24gPC0gcmVhZC5jc3YoZmlsZSA9ICIuLi9CdWxrX3NlcXVlbmNpbmcvYW5ub3RhdGlvbi9hbm5vdGF0aW9uX2J1bGsuY3N2IiAsIHJvdy5uYW1lcyA9IDEpCmBgYAoKYGBge3J9CmJ1bGtfYW5ub3RhdGlvbiR0eXBlIDwtIGZhY3Rvcih4ID0gYnVsa19hbm5vdGF0aW9uJHR5cGUgLCBsZXZlbHMgPSBjKCJOU0MiLCJOZXVyb2JsYXN0IiwiTWljcm9nbGlhIiwiRW5kb3RoZWxpYWwiKSkKYnVsa19hbm5vdGF0aW9uJGFnZSA8LSBmYWN0b3IoeCA9IGJ1bGtfYW5ub3RhdGlvbiRhZ2UgLCBsZXZlbHMgPSBjKCJ5b3VuZyIsIk1vNyIsIm9sZCIpKQpgYGAKCgpXaGljaCBjZWxsdHlwZXMgZG8gd2UgaGF2ZT8KCmBgYHtyfQpidWxrX2NlbGx0eXBlcyA8LSB1bmlxdWUoYXMuY2hhcmFjdGVyKGJ1bGtfYW5ub3RhdGlvbiR0eXBlKSkKCmJ1bGtfY2VsbHR5cGVzCmBgYAoKCgojIyBMb2FkIHRoZSBTTUFSVHNlcTIgc2VxdWVuY2luZyBUUE0gdmFsdWVzCgpgYGB7cn0Kc21hcnRzZXEyX2RhdGEgPC0gcmVhZC5jc3YoZmlsZSA9ICIuLi9TbWFydFNlcTIvY291bnRfdGFibGUvVFBNX05TQ3MuY3N2IikgJT4lIGRwbHlyOjpzZWxlY3QoLVgpICU+JSBjb2x1bW5fdG9fcm93bmFtZXModmFyID0gImVuc2VtYmxfZ2VuZV9pZCIpCgpzbWFydHNlcTJfZGF0YSA8LSBhcy5tYXRyaXgoc21hcnRzZXEyX2RhdGEpCmBgYAoKTG9nMiB0aGUgVFBNIHZhbHVlcwoKYGBge3J9CnNtYXJ0c2VxMl9kYXRhIDwtIGxvZzIoc21hcnRzZXEyX2RhdGErMSkKYGBgCgoKQWxzbyB3ZSBsb2FkIHRoZSBhbm5vdGF0aW9uCgpgYGB7cn0Kc21hcnRzZXEyX2Fubm90YXRpb24gPC0gcmVhZC5jc3YoZmlsZSA9ICIuLi9TbWFydFNlcTIvY291bnRfdGFibGUvTlNDc19hbm5vdGF0aW9uLmNzdiIgLCByb3cubmFtZXMgPSAxKQpgYGAKCmBgYHtyfQpzbWFydHNlcTJfYW5ub3RhdGlvbiR0eXBlIDwtIGZjdF9yZWNvZGUoLmYgPSBzbWFydHNlcTJfYW5ub3RhdGlvbiR0eXBlICwgcU5TQzEgPSAicTEiICAsIHFOU0MyID0gInEyIiAsIGFOU0MxID0gImExIiAsIGFOU0MyID0gImEyIikKc21hcnRzZXEyX2Fubm90YXRpb24kdHlwZSA8LSBmYWN0b3IoeCA9IHNtYXJ0c2VxMl9hbm5vdGF0aW9uJHR5cGUgLCBsZXZlbHMgPSBjKCJxTlNDMSIsInFOU0MyIiwiYU5TQzEiLCJhTlNDMiIpKQpzbWFydHNlcTJfYW5ub3RhdGlvbiRhZ2UgPC0gZmFjdG9yKHggPSBzbWFydHNlcTJfYW5ub3RhdGlvbiRhZ2UgLCBsZXZlbHMgPSBjKCJ5b3VuZyIsIm9sZCIpKQpgYGAKCldoaWNoIGNlbGx0eXBlcyBkbyB3ZSBoYXZlPwoKYGBge3J9CnNtYXJ0c2VxMl9jZWxsdHlwZV9jb2xvcnMgPC0gYyggcU5TQzEgPSAic3RlZWxibHVlIiAsIHFOU0MyID0gInN0ZWVsYmx1ZTEiICwgYU5TQzEgPSAidG9tYXRvIiAsIGFOU0MyID0gInNpZW5uYTEiKQpgYGAKCiMjIExvYWQgdGhlIDEwWCBzZXF1ZW5jaW5nIG5vcm1hbGl6ZWQgdmFsdWVzCgpgYGB7cn0Kc2V1cmF0XzEwWDIgPC0gcmVhZFJEUyhmaWxlID0gIi4uLzEwWC9zZXVyYXRfMTBYMl9jbHVzdGVyZWRfbWluXzE1MDBfbkdlbmUuUkRTIiApCmBgYAoKV2UgY2FuIGV4dHJhY3QgdGhlIG5vcm1hbGl6ZWQgZXhwcmVzc2lvbiBmcm9tIHRoZSBvYmplY3QKCmBgYHtyfQp0ZW54X2RhdGEgPC0gZXhwbTEoIHNldXJhdF8xMFgyQGRhdGEgKQoKdGVueF9kYXRhIDwtIGFzLm1hdHJpeCh0ZW54X2RhdGEpCmBgYAoKTG9nMiB0aGUgVFBNIHZhbHVlcwoKYGBge3J9CnRlbnhfZGF0YSA8LSBsb2cyKHRlbnhfZGF0YSsxKQpgYGAKCkFsc28gd2UgZXh0cmFjdCB0aGUgYW5ub3RhdGlvbiBmcm9tIHRoZSBvYmplY3QKCmBgYHtyfQp0ZW54X2Fubm90YXRpb24gPC0gRmV0Y2hEYXRhKG9iamVjdCA9IHNldXJhdF8xMFgyICwgdmFycy5hbGwgPSBjKCJjZWxsdHlwZSIsImFnZSIpICkgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigiY2VsbCIpICU+JSBkcGx5cjo6cmVuYW1lKHR5cGUgPSBjZWxsdHlwZSkKYGBgCgpgYGB7cn0KdGVueF9hbm5vdGF0aW9uJHR5cGUgPC0gZmFjdG9yKHggPSB0ZW54X2Fubm90YXRpb24kdHlwZSAsIGxldmVscyA9IGMoInFOU0MxIiwicU5TQzIiLCJhTlNDMCIsImFOU0MxIiwiYU5TQzIiLCJUQVAiLCJOQiIsIk9QQyIsIk9EIikpCnRlbnhfYW5ub3RhdGlvbiRhZ2UgPC0gZmFjdG9yKHggPSB0ZW54X2Fubm90YXRpb24kYWdlICwgbGV2ZWxzID0gYygieW91bmciLCJvbGQiKSkKYGBgCgoKV2hpY2ggY2VsbHR5cGVzIGRvIHdlIGhhdmU/CgpgYGB7cn0KdGVueF9jZWxsdHlwZV9jb2xvcnMgPC0gYyggcU5TQzEgPSAic3RlZWxibHVlIiAsIHFOU0MyID0gInN0ZWVsYmx1ZTEiICwgYU5TQzAgPSAidG9tYXRvIiAsIGFOU0MxID0gInNpZW5uYTEiLCBhTlNDMiA9ICJzaWVubmEzIiAsIFRBUCA9ICJncmVlbiIgLCBOQiA9ICJ5ZWxsb3ciICwgT1BDID0gInBpbmsiICwgT0QgPSAidmlvbGV0IikKYGBgCgpBbHNvIHdlIGNhbiBsb2FkIHRoZSBnZW5lcy50c3YgZmlsZSBmcm9tIHRoZSBDZWxsUmFuZ2VyIE91dHB1dCB3aGljaCBjb250YWlucyBhIG1hcHBpbmcgb2YgRU5TRU1CTCBHZW5lIElEcyB0byB0aGUgR2VuZSBuYW1lcyB1c2VkIGluIHRoZSAxMFggb2JqZWN0CgpgYGB7cn0KdGVueF9nZW5lXzJfZW5zZW1ibF90YmwgPC0gcmVhZC50YWJsZShmaWxlID0gIi4uLzEwWC9jb3VudF90YWJsZS9maWx0ZXJlZF9nZW5lX2JjX21hdHJpY2VzX21leC9tbTEwL2dlbmVzLnRzdiIpICU+JSBkcGx5cjo6cmVuYW1lKCBlbnNlbWJsX2dlbmVfaWQgPSBWMSAsIGdlbmVfc3ltYm9sID0gVjIgKQpgYGAKCldlIGNhbiB0cmFuc2xhdGUgYWxsIHRoZSBnZW5lIHN5bWJvbHMgdG8gRU5TRU1CTCBHZW5lIElEUwoKYGBge3J9CnRlbnhfZW5zZW1ibF9nZW5lX2lkIDwtIHVubGlzdChsYXBwbHkoIFggPSByb3duYW1lcyh0ZW54X2RhdGEpICwgRlVOID0gZnVuY3Rpb24oZ2VuZSl7CiAgICBhcy5jaGFyYWN0ZXIoIHRlbnhfZ2VuZV8yX2Vuc2VtYmxfdGJsWyB3aGljaCh0ZW54X2dlbmVfMl9lbnNlbWJsX3RibCRnZW5lX3N5bWJvbCA9PSBnZW5lKSAsICJlbnNlbWJsX2dlbmVfaWQiIF0gKVsxXSAKICB9ICkpCmBgYAoKQWRkIHRoZSBFTlNFTUJMIEdlbmUgSURzIGFzIHJvd25hbWVzCgpgYGB7cn0KdGVueF9kYXRhX2Vuc2VtYmwgPC0gdGVueF9kYXRhCgpyb3duYW1lcyh0ZW54X2RhdGFfZW5zZW1ibCkgPC0gdGVueF9lbnNlbWJsX2dlbmVfaWQKYGBgCgojIyBMb2FkIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lIGxpc3RzCgojIyMgQnVsayBzZXF1ZW5jaW5nCgojIyMjIE5TQ3MKCmBgYHtyfQpidWxrX0RFX2dlbmVzX05TQyA8LSByZWFkLmNzdihmaWxlID0gIi4uL0J1bGtfc2VxdWVuY2luZy9ERVNlcTJfcmVzdWx0c19vbGRfdnNfeW91bmcvR1BfREVTZXEyX3Jlc3VsdHNfT2xkX3ZzX1lhbmcuY3N2IiAsIHJvdy5uYW1lcyA9IDEpICU+JSBtdXRhdGUoZ2VuZV9zeW1ib2wgPSBzdHJfdG9fdGl0bGUoZ2VuZV9zeW1ib2wpICkKYGBgCgpgYGB7cn0KYnVsa19ERV9nZW5lc19OU0NfTW83X3ZzX3lvdW5nIDwtIHJlYWQuY3N2KGZpbGUgPSAiLi4vQnVsa19zZXF1ZW5jaW5nL0RFU2VxMl9yZXN1bHRzX29sZF92c195b3VuZy9HUF9ERVNlcTJfcmVzdWx0c19NN192c19Zb3VuZy5jc3YiICwgcm93Lm5hbWVzID0gMSkgICU+JSBtdXRhdGUoZ2VuZV9zeW1ib2wgPSBzdHJfdG9fdGl0bGUoZ2VuZV9zeW1ib2wpICkKYGBgCgoKIyMjIyBOZXVyb2JsYXN0cwoKYGBge3J9CmJ1bGtfREVfZ2VuZXNfTkIgPC0gcmVhZC5jc3YoZmlsZSA9ICIuLi9CdWxrX3NlcXVlbmNpbmcvREVTZXEyX3Jlc3VsdHNfb2xkX3ZzX3lvdW5nL1BTQV9ERVNlcTJfcmVzdWx0c19PbGRfdnNfWWFuZy5jc3YiICwgcm93Lm5hbWVzID0gMSkgICU+JSBtdXRhdGUoZ2VuZV9zeW1ib2wgPSBzdHJfdG9fdGl0bGUoZ2VuZV9zeW1ib2wpICkKYGBgCgojIyMjIE1pY3JvZ2xpYQoKYGBge3J9CmJ1bGtfREVfZ2VuZXNfTWljcm9nbGlhIDwtIHJlYWQuY3N2KGZpbGUgPSAiLi4vQnVsa19zZXF1ZW5jaW5nL0RFU2VxMl9yZXN1bHRzX29sZF92c195b3VuZy9DRDExYl9ERVNlcTJfcmVzdWx0c19PbGRfdnNfWWFuZy5jc3YiICwgcm93Lm5hbWVzID0gMSkgICU+JSBtdXRhdGUoZ2VuZV9zeW1ib2wgPSBzdHJfdG9fdGl0bGUoZ2VuZV9zeW1ib2wpICkKYGBgCgojIyMjIEVuZG90aGVsaWFsIENlbGxzCgpgYGB7cn0KYnVsa19ERV9nZW5lc19FbmRvdGhlbGlhbCA8LSByZWFkLmNzdihmaWxlID0gIi4uL0J1bGtfc2VxdWVuY2luZy9ERVNlcTJfcmVzdWx0c19vbGRfdnNfeW91bmcvQ0QzMV9ERVNlcTJfcmVzdWx0c19PbGRfdnNfWWFuZy5jc3YiICwgcm93Lm5hbWVzID0gMSkgICU+JSBtdXRhdGUoZ2VuZV9zeW1ib2wgPSBzdHJfdG9fdGl0bGUoZ2VuZV9zeW1ib2wpICkKYGBgCgpNYWtlIGEgbGlzdCBvZiB0aGUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzCgpgYGB7cn0KYnVsa19ERV9nZW5lcyA8LSBsaXN0KCBOU0MgPSBidWxrX0RFX2dlbmVzX05TQyAsIE5CID0gYnVsa19ERV9nZW5lc19OQiAsIE1pY3JvZ2xpYSA9IGJ1bGtfREVfZ2VuZXNfTWljcm9nbGlhICwgRW5kb3RoZWxpYWwgPSBidWxrX0RFX2dlbmVzX0VuZG90aGVsaWFsICkKYGBgCgojIyBNQSBQbG90cyAgCgpgYGB7cn0KTUFQbG90IDwtIGZ1bmN0aW9uKCB4ICwgbWFpbiA9ICIiICwgaGlnaGxpZ2h0X2dlbmVzID0gYygpICwgZGF0YSA9IE5VTEwgLCBTTUFSVHNlcTIgPSBGQUxTRSAsIGxhYmVMX3RvcF9nZW5lcyA9IEZBTFNFICwgbWF4X2ZvbGRDaGFuZ2UgPSAxMCAsIG1pbl9iYXNlTWVhbiA9IDAuMSAgLCAgbWF4X2Jhc2VNZWFuID0gMTAwMDAwMCApewogIHJlcXVpcmUoZHBseXIpCiAgcmVxdWlyZShnZ3Bsb3QyKQogIHJlcXVpcmUoZ2dyZXBlbCkKICAKICBpZighaXMubnVsbChkYXRhKSl7CiAgICByb3dtZWFuIDwtIHJvd01lYW5zKGRhdGEpCiAgICAKICAgIHJvd21lYW5fZGYgPC0gZGF0YS5mcmFtZShiYXNlTWVhbiA9IGFzLm51bWVyaWMocm93bWVhbikgLCBnZW5lX3N5bWJvbCA9IGFzLmNoYXJhY3RlciggbmFtZXMocm93bWVhbikgKSApCiAgICAKICAgIGlmKFNNQVJUc2VxMil7CiAgICAgIGNvbG5hbWVzKHJvd21lYW5fZGYpWzJdIDwtICJlbnNlbWJsX2dlbmVfaWQiCiAgICAgIAogICAgICB4IDwtIGxlZnRfam9pbih4ID0geCwgeSA9IHJvd21lYW5fZGYgLCBieSA9ICJlbnNlbWJsX2dlbmVfaWQiICkKICAgIH1lbHNlewogICAgICB4IDwtIGxlZnRfam9pbih4ID0geCwgeSA9IHJvd21lYW5fZGYgLCBieSA9ICJnZW5lX3N5bWJvbCIgKQogICAgfQogIH0KICAKICB4X3NpZyA8LSBmaWx0ZXIoIHggLCBwYWRqIDwgMC4wNSAmIGFicyhsb2cyRm9sZENoYW5nZSkgPiAxICkKICAKICBpZiggbGFiZUxfdG9wX2dlbmVzICl7IHhfbGFiZWwgPC0gZmlsdGVyKCB4X3NpZyAsICBhYnMobG9nMkZvbGRDaGFuZ2UpID4gNCApIH0KICAKICB4X2hpZ2hsaWdodCA8LSBmaWx0ZXIoIHggLCBnZW5lX3N5bWJvbCAlaW4lIGhpZ2hsaWdodF9nZW5lcyApCiAgCiAgZyA8LSBnZ3Bsb3QoZGF0YSA9IHggLCBtYXBwaW5nID0gYWVzKHggPSBiYXNlTWVhbiAsIHkgPSBsb2cyRm9sZENoYW5nZSApKSArIGdlb21fcG9pbnQoY29sb3IgPSAiZ3JleSIgKSArIGdlb21fcG9pbnQoZGF0YSA9IHhfc2lnICwgY29sb3IgPSAicmVkIiApICsgZ2VvbV9wb2ludChkYXRhID0geF9oaWdobGlnaHQgLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiY3lhbiIgICwgc2l6ZSA9IDIgLCBwY2ggPSAyMSkgCiAgCiAgaWYoIGxhYmVMX3RvcF9nZW5lcyApewogICAgZyA8LSBnICsgZ2VvbV90ZXh0X3JlcGVsKGRhdGEgPSB4X2xhYmVsICwgbWFwcGluZyA9IGFlcyggeCA9IGJhc2VNZWFuICwgeSA9IGxvZzJGb2xkQ2hhbmdlICwgbGFiZWwgPSBnZW5lX3N5bWJvbCkpCiAgfQogIAogIGcgPC0gZyArIHNjYWxlX3hfbG9nMTAobGltaXRzID0gYyhtaW5fYmFzZU1lYW4sIG1heF9iYXNlTWVhbiApLCBleHBhbmQgPSBjKDAsIDApKSArIGdndGl0bGUobWFpbikgKyB5bGltKGMoLW1heF9mb2xkQ2hhbmdlLG1heF9mb2xkQ2hhbmdlKSkKICAKICBnCn0KCmBgYAoKCiMjIyBCdWxrIHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQoKIyMjIyBOU0Mgey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9CgojIyMjIyBXaXRoIExhYmVscwoKYGBge3IgZmlnLndpZHRoPTE1fQpNQVBsb3QoeCA9IGJ1bGtfREVfZ2VuZXNfTlNDICwgbWFpbiA9ICJOU0MgKDE5IE1vbnRoIHZzIDIgTW9udGgpIiAsIGxhYmVMX3RvcF9nZW5lcyA9IFRSVUUgKQpgYGAKCgoKYGBge3IgZmlnLndpZHRoPTE1fQpNQVBsb3QoeCA9IGJ1bGtfREVfZ2VuZXNfTlNDX01vN192c195b3VuZywgbWFpbiA9ICJOU0MgKDcgTW9udGggdnMgMiBNb250aCkiICwgbGFiZUxfdG9wX2dlbmVzID0gVFJVRSkKYGBgCgojIyMjIyBXaXRob3V0IExhYmVscwoKYGBge3IgZmlnLndpZHRoPTE1fQpNQVBsb3QoeCA9IGJ1bGtfREVfZ2VuZXNfTlNDICwgbWFpbiA9ICJOU0MgKDE5IE1vbnRoIHZzIDIgTW9udGgpIiApCmBgYAoKYGBge3IgZmlnLndpZHRoPTE1fQpNQVBsb3QoeCA9IGJ1bGtfREVfZ2VuZXNfTlNDX01vN192c195b3VuZyAsIG1haW4gPSAiTlNDICg3IE1vbnRoIHZzIDIgTW9udGgpIikKYGBgCgojIyMjIE5CCgpgYGB7ciBmaWcud2lkdGg9MTV9Ck1BUGxvdCh4ID0gYnVsa19ERV9nZW5lc19OQiAsIG1haW4gPSAiTkIiKQpgYGAKCmBgYHtyIGZpZy53aWR0aD0xNX0KTUFQbG90KHggPSBidWxrX0RFX2dlbmVzX05CICwgbWFpbiA9ICJOQiIgLCBsYWJlTF90b3BfZ2VuZXMgPSBUUlVFKQpgYGAKCgojIyMjIE1pY3JvZ2xpYQoKYGBge3IgZmlnLndpZHRoPTE1fQpNQVBsb3QoeCA9IGJ1bGtfREVfZ2VuZXNfTWljcm9nbGlhICwgbWFpbiA9ICJNaWNyb2dsaWEiKQpgYGAKCmBgYHtyIGZpZy53aWR0aD0xNX0KTUFQbG90KHggPSBidWxrX0RFX2dlbmVzX01pY3JvZ2xpYSAsIG1haW4gPSAiTWljcm9nbGlhIiAsIGxhYmVMX3RvcF9nZW5lcyA9IFRSVUUpCmBgYAoKIyMjIyBFbmRvdGhlbGlhbCBjZWxscwoKYGBge3IgZmlnLndpZHRoPTE1fQpNQVBsb3QoeCA9IGJ1bGtfREVfZ2VuZXNfRW5kb3RoZWxpYWwgLCBtYWluID0gIkVuZG90aGVsaWFsIikKYGBgCgpgYGB7ciBmaWcud2lkdGg9MTV9Ck1BUGxvdCh4ID0gYnVsa19ERV9nZW5lc19FbmRvdGhlbGlhbCAsIG1haW4gPSAiRW5kb3RoZWxpYWwiICwgbGFiZUxfdG9wX2dlbmVzID0gVFJVRSkKYGBgCgoKIyMgSGVhdG1hcHMgb2YgZ2VuZSBleHByZXNzaW9uCgpgYGB7cn0KSGVhdG1hcEdlbmVzIDwtIGZ1bmN0aW9uKCB4ICwgZ2VuZXMgPSBjKCkgLCB0b3BWYXIgPSBOVUxMICwgbWFpbiA9ICIiICwgYW5ub3RhdGlvbiAsIGFubm90YXRpb25fY29sb3JzICwgY2x1c3Rlcl9jb2xzID0gRkFMU0UgLCBjbHVzdGVyX3Jvd3MgPSBUUlVFICwgc2hvd19yb3duYW1lcyA9IEZBTFNFICwgc2hvd19jb2xuYW1lcyA9IEZBTFNFICwgYXJyYW5nZV9ieV9hZ2UgPSBGQUxTRSAsIGtlZXBfbm9uX2V4cHJlc3NlZCA9IEZBTFNFICl7CiAgcmVxdWlyZSgicGhlYXRtYXAiKQogIHJlcXVpcmUoInZpcmlkaXNMaXRlIikKICAKICAjIFRoZSBjZWxscyBpbiB0aGUgYW5ub3RhdGlvbiBhcmUgdGFrZW4gYXMgdGhlIGNlbGxzIHRvIGJlIHBsb3R0ZWQKICBhbm5vdGF0aW9uIDwtIHJlbW92ZV9yb3duYW1lcyhkZiA9IGFubm90YXRpb24pCiAgCiAgaWYoISBhcnJhbmdlX2J5X2FnZSl7CiAgICBhbm5vdGF0aW9uIDwtIGFubm90YXRpb24gJT4lIGFycmFuZ2UoIHR5cGUgLCBhZ2UpCiAgfWVsc2V7CiAgICBhbm5vdGF0aW9uIDwtIGFubm90YXRpb24gJT4lIGFycmFuZ2UoIGFnZSAsIHR5cGUpCiAgfQogIAogIGlmKCJjZWxsIiAlaW4lIGNvbG5hbWVzKGFubm90YXRpb24pKXsgbmFtZXNfY29scyA8LSBhbm5vdGF0aW9uICU+JSBwdWxsKCJjZWxsIikgJT4lIGFzLmNoYXJhY3RlcigpIH0KICBpZigic2FtcGxlIiAlaW4lIGNvbG5hbWVzKGFubm90YXRpb24pKXsgbmFtZXNfY29scyA8LSBhbm5vdGF0aW9uICU+JSBwdWxsKCJzYW1wbGUiKSAlPiUgYXMuY2hhcmFjdGVyKCkgfQogIAogICMgVGhlbiB0aGUgYW5ub3RhdGlvbiBkYXRhLmZyYW1lIGlzIHByZXBhcmVkIGFzIG5lZWRlZCBmb3IgdGhlIHBoZWF0bWFwIGZ1bmN0aW9uIHdpdGggdGhlIGNlbGwgbmFtZXMgYXMgcm93bmFtZXMKICBpZigiY2VsbCIgJWluJSBjb2xuYW1lcyhhbm5vdGF0aW9uKSl7YW5ub3RhdGlvbiA8LSBjb2x1bW5fdG9fcm93bmFtZXMoZGYgPSBhbm5vdGF0aW9uICwgdmFyID0gImNlbGwiKX0KICBpZigic2FtcGxlIiAlaW4lIGNvbG5hbWVzKGFubm90YXRpb24pKXthbm5vdGF0aW9uIDwtIGNvbHVtbl90b19yb3duYW1lcyhkZiA9IGFubm90YXRpb24gLCB2YXIgPSAic2FtcGxlIil9CiAgIyBXZSBzZXQgdGhlIG9yZGVyIG9mIGNvbHVtbnMgb2YgdGhlIGFubm90YXRpb24gdG8gYmUgZmlyc3QgYWdlIHRoZW4gdHlwZSAKICBhbm5vdGF0aW9uIDwtIGFubm90YXRpb25bLGMoImFnZSIsInR5cGUiKV0KICAKICAjIFdlIGNoZWNrIGZvciB0aGUgZ2VuZXMgZnJvbSB0aGUgbGlzdCB0aGF0IGFyZSBhdmFpbGFibGUgaW4gb3VyIGRhdGEKICBnZW5lcyA8LSBhcy5jaGFyYWN0ZXIoZ2VuZXMpCiAgZ2VuZXNfYXZhaWwgPC0gZ2VuZXNbZ2VuZXMgJWluJSByb3duYW1lcyh4KV0KICAKICBpZighaXMubnVsbCh0b3BWYXIpKXsKICAgIHggPC0geFssbmFtZXNfY29sc10KICAgIAogICAgdmFyX2dlbmVzIDwtIG5hbWVzKHNvcnQoYXBwbHkoWCA9IHgsIE1BUkdJTiA9IDEsIEZVTiA9IHZhciksIGRlY3JlYXNpbmcgPSBUUlVFKVsxOnRvcFZhcl0pCiAgICB2YXJfZ2VuZXMgPC0gdmFyX2dlbmVzWyFpcy5uYSh2YXJfZ2VuZXMpXQogICAgCiAgICB4X21hdCA8LSB4W3Zhcl9nZW5lcyAsXSAKICB9ZWxzZXsKICAgICMgT25seSB0YWtlIHRoZSB2YWx1ZXMgZm9yIGdlbmVzIHRoYXQgYXJlIGZyb20gdGhlIGdlbmUgbGlzdAogICAgeF9tYXQgPC0geFtnZW5lc19hdmFpbCxuYW1lc19jb2xzXQogIH0KCiAgIyBEaWNhcmQgYWxsIHRoZSBub24gZXhwcmVzc2VkIGdlbmVzIGFzIHRoZXkgd291bGQgY3JlYXRlIHByb2JsZW1zIGR1cmluZyB0aGUgY2x1c3RlcmluZyBwcm9jZXNzCiAgaWYoIWtlZXBfbm9uX2V4cHJlc3NlZCl7CiAgICB4IDwtIHhbYXBwbHkoWCA9IHgsIE1BUkdJTiA9IDEsIEZVTiA9IHZhcikgPiAwLF0KICB9CiAgaWYoa2VlcF9ub25fZXhwcmVzc2VkICYgY2x1c3Rlcl9yb3dzKXt3YXJuaW5nKCJSb3cgY2x1c3RlcmluZyBjYW5ub3QgYmUgdXNlZCBpZiB1bmV4cHJlc3NlZCBnZW5lcyBhcmUga2VwdCEiKX0KICAKICBwaGVhdG1hcCgKICAgIG1hdCA9IHhfbWF0ICwKICAgIGNsdXN0ZXJfcm93cyA9IGNsdXN0ZXJfcm93cyAsIAogICAgY2x1c3Rlcl9jb2xzID0gY2x1c3Rlcl9jb2xzICwKICAgIGNvbG9yID0gdmlyaWRpcyhuID0gMTAwKSwKICAgIHNob3dfcm93bmFtZXMgPSBzaG93X3Jvd25hbWVzLAogICAgc2hvd19jb2xuYW1lcyA9IHNob3dfY29sbmFtZXMgLAogICAgY2x1c3RlcmluZ19tZXRob2QgPSAiY29tcGxldGUiLCAKICAgIGNsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cyA9ICJldWNsaWRlYW4iICwKICAgIGJvcmRlcl9jb2xvciA9IE5BLAogICAgc2NhbGUgPSAibm9uZSIsCiAgICBhbm5vdGF0aW9uX2NvbCA9IGFubm90YXRpb24sCiAgICBtYWluID0gbWFpbiwKICAgIGZvbnRzaXplID0gNSwKICAgIGFubm90YXRpb25fY29sb3JzID0gYW5ub3RhdGlvbl9jb2xvcnMKKQp9CmBgYAoKIyMjIFNNQVJUc2VxMiBkYXRhIHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQoKIyMjIyBUb3AgVmFyaWFibGUgR2VuZXMgaW4gc3VicG9wdWxhdGlvbnMgey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9CgojIyMjIyBxTlNDMTogVG9wIDUwMDAgdmFyaWFibGUgZ2VuZXMgCgpgYGB7cn0KSGVhdG1hcEdlbmVzKHggPSBzbWFydHNlcTJfZGF0YSAsIHRvcFZhciA9IDUwMDAgLCBtYWluID0gIlNNQVJUc2VxMjogcU5TQzEgeW91bmcgYW5kIG9sZCBUb3AgdmFyaWFibGUgZ2VuZXMiICwgYW5ub3RhdGlvbiA9IGZpbHRlcihzbWFydHNlcTJfYW5ub3RhdGlvbiAsIHR5cGUgJWluJSBjKCJxTlNDMSIpKSAsIGFubm90YXRpb25fY29sb3JzID0gbGlzdChhZ2UgPSBhZ2VfY29sb3JzICwgdHlwZSA9IHNtYXJ0c2VxMl9jZWxsdHlwZV9jb2xvcnMgKSAsIGNsdXN0ZXJfY29scyA9IFRSVUUgLCBjbHVzdGVyX3Jvd3MgPSBUUlVFKQpgYGAKCiMjIyMjIHFOU0MyOiBUb3AgNTAwMCB2YXJpYWJsZSBnZW5lcyAKCmBgYHtyfQpIZWF0bWFwR2VuZXMoeCA9IHNtYXJ0c2VxMl9kYXRhICwgdG9wVmFyID0gNTAwMCAsIG1haW4gPSAiU01BUlRzZXEyOiBxTlNDMiB5b3VuZyBhbmQgb2xkIFRvcCB2YXJpYWJsZSBnZW5lcyIgLCBhbm5vdGF0aW9uID0gZmlsdGVyKHNtYXJ0c2VxMl9hbm5vdGF0aW9uICwgdHlwZSAlaW4lIGMoInFOU0MyIikpICwgYW5ub3RhdGlvbl9jb2xvcnMgPSBsaXN0KGFnZSA9IGFnZV9jb2xvcnMgLCB0eXBlID0gc21hcnRzZXEyX2NlbGx0eXBlX2NvbG9ycyApICwgY2x1c3Rlcl9jb2xzID0gVFJVRSAsIGNsdXN0ZXJfcm93cyA9IFRSVUUpCmBgYAoKIyMjIyMgYU5TQzE6IFRvcCA1MDAwIHZhcmlhYmxlIGdlbmVzIAoKYGBge3J9CkhlYXRtYXBHZW5lcyh4ID0gc21hcnRzZXEyX2RhdGEgLCB0b3BWYXIgPSA1MDAwICwgbWFpbiA9ICJTTUFSVHNlcTI6IGFOU0MxIHlvdW5nIGFuZCBvbGQgVG9wIHZhcmlhYmxlIGdlbmVzIiAsIGFubm90YXRpb24gPSBmaWx0ZXIoc21hcnRzZXEyX2Fubm90YXRpb24gLCB0eXBlICVpbiUgYygiYU5TQzEiKSkgLCBhbm5vdGF0aW9uX2NvbG9ycyA9IGxpc3QoYWdlID0gYWdlX2NvbG9ycyAsIHR5cGUgPSBzbWFydHNlcTJfY2VsbHR5cGVfY29sb3JzICkgLCBjbHVzdGVyX2NvbHMgPSBUUlVFICwgY2x1c3Rlcl9yb3dzID0gVFJVRSkKYGBgCgojIyMjIyBhTlNDMjogVG9wIDUwMDAgdmFyaWFibGUgZ2VuZXMKCmBgYHtyfQpIZWF0bWFwR2VuZXMoeCA9IHNtYXJ0c2VxMl9kYXRhICwgdG9wVmFyID0gNTAwMCAsIG1haW4gPSAiU01BUlRzZXEyOiBhTlNDMiB5b3VuZyBhbmQgb2xkIFRvcCB2YXJpYWJsZSBnZW5lcyIgLCBhbm5vdGF0aW9uID0gZmlsdGVyKHNtYXJ0c2VxMl9hbm5vdGF0aW9uICwgdHlwZSAlaW4lIGMoImFOU0MyIikpICwgYW5ub3RhdGlvbl9jb2xvcnMgPSBsaXN0KGFnZSA9IGFnZV9jb2xvcnMgLCB0eXBlID0gc21hcnRzZXEyX2NlbGx0eXBlX2NvbG9ycyApICwgY2x1c3Rlcl9jb2xzID0gVFJVRSAsIGNsdXN0ZXJfcm93cyA9IFRSVUUpCmBgYAoKIyMjIDEwWCBkYXRhIHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQoKIyMjIyBUb3AgVmFyaWFibGUgR2VuZXMgaW4gc3VicG9wdWxhdGlvbnMgey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9CgojIyMjIyBxTlNDMTogVG9wIDUwMDAgdmFyaWFibGUgZ2VuZXMgCgpgYGB7cn0KIyBIZWF0bWFwR2VuZXMoeCA9IHRlbnhfZGF0YV9lbnNlbWJsICwgdG9wVmFyID0gNTAwMCAsIG1haW4gPSAiMTBYOiBxTlNDMSB5b3VuZyBhbmQgb2xkIFRvcCB2YXJpYWJsZSBnZW5lcyIgLCBhbm5vdGF0aW9uID0gZmlsdGVyKHRlbnhfYW5ub3RhdGlvbiAsIHR5cGUgJWluJSBjKCJxTlNDMSIpKSAsIGFubm90YXRpb25fY29sb3JzID0gbGlzdChhZ2UgPSBhZ2VfY29sb3JzICwgdHlwZSA9IHRlbnhfY2VsbHR5cGVfY29sb3JzICkgLCBjbHVzdGVyX2NvbHMgPSBUUlVFICwgY2x1c3Rlcl9yb3dzID0gVFJVRSkKYGBgCgojIyMjIyBxTlNDMjogVG9wIDUwMDAgdmFyaWFibGUgZ2VuZXMgCgpgYGB7cn0KIyBIZWF0bWFwR2VuZXMoeCA9IHRlbnhfZGF0YV9lbnNlbWJsICwgdG9wVmFyID0gNTAwMCAsIG1haW4gPSAiMTBYOiBxTlNDMiB5b3VuZyBhbmQgb2xkIFRvcCB2YXJpYWJsZSBnZW5lcyIgLCBhbm5vdGF0aW9uID0gZmlsdGVyKHRlbnhfYW5ub3RhdGlvbiAsIHR5cGUgJWluJSBjKCJxTlNDMiIpKSAsIGFubm90YXRpb25fY29sb3JzID0gbGlzdChhZ2UgPSBhZ2VfY29sb3JzICwgdHlwZSA9IHRlbnhfY2VsbHR5cGVfY29sb3JzICkgLCBjbHVzdGVyX2NvbHMgPSBUUlVFICwgY2x1c3Rlcl9yb3dzID0gVFJVRSApCmBgYAoKIyMjIyMgYU5TQzA6IFRvcCA1MDAwIHZhcmlhYmxlIGdlbmVzIAoKYGBge3J9CiMgSGVhdG1hcEdlbmVzKHggPSB0ZW54X2RhdGFfZW5zZW1ibCAsIHRvcFZhciA9IDUwMDAgLCBtYWluID0gIjEwWDogYU5TQzAgeW91bmcgYW5kIG9sZCBUb3AgdmFyaWFibGUgZ2VuZXMiICwgYW5ub3RhdGlvbiA9IGZpbHRlcih0ZW54X2Fubm90YXRpb24gLCB0eXBlICVpbiUgYygiYU5TQzAiKSkgLCBhbm5vdGF0aW9uX2NvbG9ycyA9IGxpc3QoYWdlID0gYWdlX2NvbG9ycyAsIHR5cGUgPSB0ZW54X2NlbGx0eXBlX2NvbG9ycyApICwgY2x1c3Rlcl9jb2xzID0gVFJVRSAsIGNsdXN0ZXJfcm93cyA9IFRSVUUpCmBgYAoKIyMjIyMgYU5TQzE6IFRvcCA1MDAwIHZhcmlhYmxlIGdlbmVzIAoKYGBge3J9CiMgSGVhdG1hcEdlbmVzKHggPSB0ZW54X2RhdGFfZW5zZW1ibCAsIHRvcFZhciA9IDUwMDAgLCBtYWluID0gIjEwWDogYU5TQzEgeW91bmcgYW5kIG9sZCBUb3AgdmFyaWFibGUgZ2VuZXMiICwgYW5ub3RhdGlvbiA9IGZpbHRlcih0ZW54X2Fubm90YXRpb24gLCB0eXBlICVpbiUgYygiYU5TQzEiKSkgLCBhbm5vdGF0aW9uX2NvbG9ycyA9IGxpc3QoYWdlID0gYWdlX2NvbG9ycyAsIHR5cGUgPSB0ZW54X2NlbGx0eXBlX2NvbG9ycyApICwgY2x1c3Rlcl9jb2xzID0gVFJVRSAsIGNsdXN0ZXJfcm93cyA9IFRSVUUpCmBgYAoKIyMjIyMgYU5TQzI6IFRvcCA1MDAwIHZhcmlhYmxlIGdlbmVzCgpgYGB7cn0KIyBIZWF0bWFwR2VuZXMoeCA9IHRlbnhfZGF0YV9lbnNlbWJsICwgdG9wVmFyID0gNTAwMCAsIG1haW4gPSAiMTBYOiBhTlNDMiB5b3VuZyBhbmQgb2xkIFRvcCB2YXJpYWJsZSBnZW5lcyIgLCBhbm5vdGF0aW9uID0gZmlsdGVyKHRlbnhfYW5ub3RhdGlvbiAsIHR5cGUgJWluJSBjKCJhTlNDMiIpKSAsIGFubm90YXRpb25fY29sb3JzID0gbGlzdChhZ2UgPSBhZ2VfY29sb3JzICwgdHlwZSA9IHRlbnhfY2VsbHR5cGVfY29sb3JzICkgLCBjbHVzdGVyX2NvbHMgPSBUUlVFICwgY2x1c3Rlcl9yb3dzID0gVFJVRSkKYGBgCiMjIyMjIFRBUDogVG9wIDUwMDAgdmFyaWFibGUgZ2VuZXMKCmBgYHtyfQojIEhlYXRtYXBHZW5lcyh4ID0gdGVueF9kYXRhX2Vuc2VtYmwgLCB0b3BWYXIgPSA1MDAwICwgbWFpbiA9ICIxMFg6IFRBUCB5b3VuZyBhbmQgb2xkIFRvcCB2YXJpYWJsZSBnZW5lcyIgLCBhbm5vdGF0aW9uID0gZmlsdGVyKHRlbnhfYW5ub3RhdGlvbiAsIHR5cGUgJWluJSBjKCJUQVAiKSkgLCBhbm5vdGF0aW9uX2NvbG9ycyA9IGxpc3QoYWdlID0gYWdlX2NvbG9ycyAsIHR5cGUgPSB0ZW54X2NlbGx0eXBlX2NvbG9ycyApICwgY2x1c3Rlcl9jb2xzID0gVFJVRSAsIGNsdXN0ZXJfcm93cyA9IFRSVUUpCmBgYAoKIyMjIyMgTkI6IFRvcCA1MDAwIHZhcmlhYmxlIGdlbmVzCgpgYGB7cn0KIyBIZWF0bWFwR2VuZXMoeCA9IHRlbnhfZGF0YV9lbnNlbWJsICwgdG9wVmFyID0gNTAwMCAsIG1haW4gPSAiMTBYOiBOQiB5b3VuZyBhbmQgb2xkIFRvcCB2YXJpYWJsZSBnZW5lcyIgLCBhbm5vdGF0aW9uID0gZmlsdGVyKHRlbnhfYW5ub3RhdGlvbiAsIHR5cGUgJWluJSBjKCJOQiIpKSAsIGFubm90YXRpb25fY29sb3JzID0gbGlzdChhZ2UgPSBhZ2VfY29sb3JzICwgdHlwZSA9IHRlbnhfY2VsbHR5cGVfY29sb3JzICkgLCBjbHVzdGVyX2NvbHMgPSBUUlVFICwgY2x1c3Rlcl9yb3dzID0gVFJVRSkKYGBgCgojIyMjIyBPUEM6IFRvcCA1MDAwIHZhcmlhYmxlIGdlbmVzCgpgYGB7cn0KIyBIZWF0bWFwR2VuZXMoeCA9IHRlbnhfZGF0YV9lbnNlbWJsICwgdG9wVmFyID0gNTAwMCAsIG1haW4gPSAiMTBYOiBPUEMgeW91bmcgYW5kIG9sZCBUb3AgdmFyaWFibGUgZ2VuZXMiICwgYW5ub3RhdGlvbiA9IGZpbHRlcih0ZW54X2Fubm90YXRpb24gLCB0eXBlICVpbiUgYygiT1BDIikpICwgYW5ub3RhdGlvbl9jb2xvcnMgPSBsaXN0KGFnZSA9IGFnZV9jb2xvcnMgLCB0eXBlID0gdGVueF9jZWxsdHlwZV9jb2xvcnMgKSAsIGNsdXN0ZXJfY29scyA9IFRSVUUgLCBjbHVzdGVyX3Jvd3MgPSBUUlVFKQpgYGAKCiMjIyMjIE9EOiBUb3AgNTAwMCB2YXJpYWJsZSBnZW5lcwoKYGBge3J9CiMgSGVhdG1hcEdlbmVzKHggPSB0ZW54X2RhdGFfZW5zZW1ibCAsIHRvcFZhciA9IDUwMDAgLCBtYWluID0gIjEwWDogT0QgeW91bmcgYW5kIG9sZCBUb3AgdmFyaWFibGUgZ2VuZXMiICwgYW5ub3RhdGlvbiA9IGZpbHRlcih0ZW54X2Fubm90YXRpb24gLCB0eXBlICVpbiUgYygiT0QiKSkgLCBhbm5vdGF0aW9uX2NvbG9ycyA9IGxpc3QoYWdlID0gYWdlX2NvbG9ycyAsIHR5cGUgPSB0ZW54X2NlbGx0eXBlX2NvbG9ycyApICwgY2x1c3Rlcl9jb2xzID0gVFJVRSAsIGNsdXN0ZXJfcm93cyA9IFRSVUUpCmBgYAoKCiMjIENvbXBhcmUgcHNldWRvLWJ1bGsgU2luZ2xlIENlbGwgUk5BLXNlcSB3aXRoIGJ1bGsgc2FtcGxlcyAtIG1lYW4gVFBNIHZhbHVlcwoKYGBge3J9ClBzZXVkb2J1bGtfYnVsa19jb21wYXJlIDwtIGZ1bmN0aW9uKCBwc2V1ZG9idWxrX2RhdGEgPSBOVUxMICwgcHNldWRvYnVsa19jZWxsdHlwZSA9ICJ4IiAsIGJ1bGtfY2VsbHR5cGUgPSAieSIgLCBidWxrX2RhdGEgPSBOVUxMICwgYW5ub3RhdGlvbl9wc2V1ZG9idWxrID0gTlVMTCAsIGFubm90YXRpb25fYnVsayA9IE5VTEwgLCBjZWxsdHlwZV9idWxrID0gTlVMTCAgLCBtYWluID0gIiIgKXsKICByZXF1aXJlKCJnZ3Bsb3QyIikKICByZXF1aXJlKCJkcGx5ciIpCiAgcmVxdWlyZSgidGliYmxlIikKICAKICBpZiggaXMubnVsbChwc2V1ZG9idWxrX2RhdGEpIHwgaXMubnVsbChidWxrX2RhdGEpICl7IHN0b3AoIllvdSBuZWVkIHRvIHN1cHBseSBhbGwgZGF0YSAhIikgfQogIGlmKCBpcy5udWxsKGFubm90YXRpb25fcHNldWRvYnVsaykgfCBpcy5udWxsKGFubm90YXRpb25fcHNldWRvYnVsaykgKXtzdG9wKCJZb3UgbmVlZCB0byBzdXBwbHkgdGhlIGFubm90YXRpb24gISIpfQogIAogIAogICMjIFN0cmlwIExvZzIgdHJhbnNmb3JtIG9mIHRoZSBkYXRhCiAgcHNldWRvYnVsa19kYXRhIDwtIDJeKHBzZXVkb2J1bGtfZGF0YSktMQogIGJ1bGtfZGF0YSA8LSAyXihidWxrX2RhdGEpLTEKICAKICBwc2V1ZG9idWxrX2RhdGEgPC0gcHNldWRvYnVsa19kYXRhWyFpcy5uYShyb3duYW1lcyhwc2V1ZG9idWxrX2RhdGEpKSxdCiAgYnVsa19kYXRhIDwtIGJ1bGtfZGF0YVshaXMubmEocm93bmFtZXMoYnVsa19kYXRhKSksXQogIAogIHBzZXVkb2J1bGtfZGF0YSA8LSBwc2V1ZG9idWxrX2RhdGFbLGFubm90YXRpb25fcHNldWRvYnVsayRjZWxsXQogIGJ1bGtfZGF0YSA8LSBidWxrX2RhdGFbLGFubm90YXRpb25fYnVsayRzYW1wbGVdCiAgICAKICBwc2V1ZG9idWxrX2RhdGFfY29tYiA8LSBkYXRhLmZyYW1lKCBleHByX3ggPSBsb2cyKHJvd01lYW5zKHBzZXVkb2J1bGtfZGF0YSkrMSkgLCBlbnNlbWJsX2dlbmVfaWQgPSByb3duYW1lcyhwc2V1ZG9idWxrX2RhdGEpICkKICBidWxrX2RhdGFfY29tYiA8LSBkYXRhLmZyYW1lKCBleHByX3kgPSBsb2cyKHJvd01lYW5zKGJ1bGtfZGF0YSkrMSkgLCBlbnNlbWJsX2dlbmVfaWQgPSByb3duYW1lcyhidWxrX2RhdGEpICkKCiAgeCA8LSBmdWxsX2pvaW4oeCA9IHBzZXVkb2J1bGtfZGF0YV9jb21iICwgeSA9IGJ1bGtfZGF0YV9jb21iICwgYnkgPSAiZW5zZW1ibF9nZW5lX2lkIiApCiAgCiAgZyA8LSBnZ3Bsb3QoZGF0YSA9IHggLCBtYXBwaW5nID0gYWVzKHggPSBleHByX3ggLCB5ID0gZXhwcl95KSkgKyBnZW9tX3BvaW50KGFscGhhID0gMC41KSArIGNvb3JkX2VxdWFsKCkgKyBsYWJzKHggPSBwc2V1ZG9idWxrX2NlbGx0eXBlICwgeSA9IGJ1bGtfY2VsbHR5cGUgKSArIGdndGl0bGUobWFpbikKICAKICBnCiAgfQpgYGAKCiMjIyBTTUFSVHNlcTIKCmBgYHtyfQpQc2V1ZG9idWxrX2J1bGtfY29tcGFyZShwc2V1ZG9idWxrX2RhdGEgPSBzbWFydHNlcTJfZGF0YSAsIGJ1bGtfZGF0YSA9IGJ1bGtfZGF0YSAsIGFubm90YXRpb25fcHNldWRvYnVsayA9IGZpbHRlcihzbWFydHNlcTJfYW5ub3RhdGlvbiwgdHlwZSAlaW4lIGMoInFOU0MxIiwicU5TQzIiLCJhTlNDMSIsImFOU0MyIiksIGFnZSA9PSAieW91bmciKSAsIGFubm90YXRpb25fYnVsayA9IGZpbHRlcihidWxrX2Fubm90YXRpb24sIHR5cGUgJWluJSBjKCJOU0MiKSAsIGFnZSA9PSAieW91bmciKSwgcHNldWRvYnVsa19jZWxsdHlwZSA9ICJwc2V1ZG8tYnVsayBzYW1wbGVzOiBOU0MgKGxvZzIoVFBNKzEpKSIgLCBidWxrX2NlbGx0eXBlID0gIiBidWxrIHNhbXBsZXM6IE5TQyAobG9nMihUUE0rMSkpIiAsIG1haW4gPSAiWW91bmcgTlNDOiBwc2V1ZG8tYnVsayB2cyBidWxrIiApCmBgYAoKYGBge3J9ClBzZXVkb2J1bGtfYnVsa19jb21wYXJlKHBzZXVkb2J1bGtfZGF0YSA9IHNtYXJ0c2VxMl9kYXRhICwgYnVsa19kYXRhID0gYnVsa19kYXRhICwgYW5ub3RhdGlvbl9wc2V1ZG9idWxrID0gZmlsdGVyKHNtYXJ0c2VxMl9hbm5vdGF0aW9uLCB0eXBlICVpbiUgYygicU5TQzEiLCJxTlNDMiIsImFOU0MxIiwiYU5TQzIiKSwgYWdlID09ICJvbGQiKSAsIGFubm90YXRpb25fYnVsayA9IGZpbHRlcihidWxrX2Fubm90YXRpb24sIHR5cGUgJWluJSBjKCJOU0MiKSAsIGFnZSA9PSAieW91bmciKSwgcHNldWRvYnVsa19jZWxsdHlwZSA9ICJwc2V1ZG8tYnVsayBzYW1wbGVzOiBOU0MgKGxvZzIoVFBNKzEpKSIgLCBidWxrX2NlbGx0eXBlID0gIiBidWxrIHNhbXBsZXM6IE5TQyAobG9nMihUUE0rMSkpIiAsIG1haW4gPSAiTlNDOiBvbGQgcHNldWRvLWJ1bGsgKFNNQVJUc2VxMikgdnMgeW91bmcgYnVsayIgKQpgYGAKCmBgYHtyfQpQc2V1ZG9idWxrX2J1bGtfY29tcGFyZShwc2V1ZG9idWxrX2RhdGEgPSBzbWFydHNlcTJfZGF0YSAsIGJ1bGtfZGF0YSA9IGJ1bGtfZGF0YSAsIGFubm90YXRpb25fcHNldWRvYnVsayA9IGZpbHRlcihzbWFydHNlcTJfYW5ub3RhdGlvbiwgdHlwZSAlaW4lIGMoInFOU0MxIiwicU5TQzIiLCJhTlNDMSIsImFOU0MyIiksIGFnZSA9PSAieW91bmciKSAsIGFubm90YXRpb25fYnVsayA9IGZpbHRlcihidWxrX2Fubm90YXRpb24sIHR5cGUgJWluJSBjKCJOU0MiKSAsIGFnZSA9PSAib2xkIiksIHBzZXVkb2J1bGtfY2VsbHR5cGUgPSAicHNldWRvLWJ1bGsgc2FtcGxlczogTlNDIChsb2cyKFRQTSsxKSkiICwgYnVsa19jZWxsdHlwZSA9ICIgYnVsayBzYW1wbGVzOiBOU0MgKGxvZzIoVFBNKzEpKSIgLCBtYWluID0gIk5TQzogeW91bmcgcHNldWRvLWJ1bGsgKFNNQVJUc2VxMikgdnMgb2xkIGJ1bGsiICkKYGBgCgpgYGB7cn0KUHNldWRvYnVsa19idWxrX2NvbXBhcmUocHNldWRvYnVsa19kYXRhID0gc21hcnRzZXEyX2RhdGEgLCBidWxrX2RhdGEgPSBidWxrX2RhdGEgLCBhbm5vdGF0aW9uX3BzZXVkb2J1bGsgPSBmaWx0ZXIoc21hcnRzZXEyX2Fubm90YXRpb24sIHR5cGUgJWluJSBjKCJxTlNDMSIsInFOU0MyIiwiYU5TQzEiLCJhTlNDMiIpLCBhZ2UgPT0gIm9sZCIpICwgYW5ub3RhdGlvbl9idWxrID0gZmlsdGVyKGJ1bGtfYW5ub3RhdGlvbiwgdHlwZSAlaW4lIGMoIk5TQyIpICwgYWdlID09ICJvbGQiKSwgcHNldWRvYnVsa19jZWxsdHlwZSA9ICJwc2V1ZG8tYnVsayBzYW1wbGVzOiBOU0MgKGxvZzIoVFBNKzEpKSIgLCBidWxrX2NlbGx0eXBlID0gIiBidWxrIHNhbXBsZXM6IE5TQyAobG9nMihUUE0rMSkpIiAsIG1haW4gPSAiT2xkIE5TQzogcHNldWRvLWJ1bGsgKFNNQVJUc2VxMikgdnMgYnVsayIgKQpgYGAKCiMjIyAxMFgKCmBgYHtyfQpQc2V1ZG9idWxrX2J1bGtfY29tcGFyZShwc2V1ZG9idWxrX2RhdGEgPSB0ZW54X2RhdGFfZW5zZW1ibCAsIGJ1bGtfZGF0YSA9IGJ1bGtfZGF0YSAsIGFubm90YXRpb25fcHNldWRvYnVsayA9IGZpbHRlcih0ZW54X2Fubm90YXRpb24sIHR5cGUgJWluJSBjKCJxTlNDMSIsInFOU0MyIiwiYU5TQzAiLCJhTlNDMSIsImFOU0MyIiksIGFnZSA9PSAib2xkIikgLCBhbm5vdGF0aW9uX2J1bGsgPSBmaWx0ZXIoYnVsa19hbm5vdGF0aW9uLCB0eXBlICVpbiUgYygiTlNDIikgLCBhZ2UgPT0gIm9sZCIpLCBwc2V1ZG9idWxrX2NlbGx0eXBlID0gInBzZXVkby1idWxrIHNhbXBsZXM6IE5TQyAobG9nMihleHByZXNzaW9uKzEpKSIgLCBidWxrX2NlbGx0eXBlID0gIiBidWxrIHNhbXBsZXM6IE5TQyAobG9nMihUUE0rMSkpIiAsIG1haW4gPSAiT2xkIE5TQzogcHNldWRvLWJ1bGsgKDEwWCkgdnMgYnVsayIgKQpgYGAKCmBgYHtyfQpQc2V1ZG9idWxrX2J1bGtfY29tcGFyZShwc2V1ZG9idWxrX2RhdGEgPSB0ZW54X2RhdGFfZW5zZW1ibCAsIGJ1bGtfZGF0YSA9IGJ1bGtfZGF0YSAsIGFubm90YXRpb25fcHNldWRvYnVsayA9IGZpbHRlcih0ZW54X2Fubm90YXRpb24sIHR5cGUgJWluJSBjKCJxTlNDMSIsInFOU0MyIiwiYU5TQzAiLCJhTlNDMSIsImFOU0MyIiksIGFnZSA9PSAib2xkIikgLCBhbm5vdGF0aW9uX2J1bGsgPSBmaWx0ZXIoYnVsa19hbm5vdGF0aW9uLCB0eXBlICVpbiUgYygiTlNDIikgLCBhZ2UgPT0gInlvdW5nIiksIHBzZXVkb2J1bGtfY2VsbHR5cGUgPSAicHNldWRvLWJ1bGsgc2FtcGxlczogTlNDIChsb2cyKGV4cHJlc3Npb24rMSkpIiAsIGJ1bGtfY2VsbHR5cGUgPSAiIGJ1bGsgc2FtcGxlczogTlNDIChsb2cyKFRQTSsxKSkiICwgbWFpbiA9ICJOU0M6IE9sZCBwc2V1ZG8tYnVsayAoMTBYKSB2cyB5b3VuZyBidWxrIiApCmBgYAoKYGBge3J9ClBzZXVkb2J1bGtfYnVsa19jb21wYXJlKHBzZXVkb2J1bGtfZGF0YSA9IHRlbnhfZGF0YV9lbnNlbWJsICwgYnVsa19kYXRhID0gYnVsa19kYXRhICwgYW5ub3RhdGlvbl9wc2V1ZG9idWxrID0gZmlsdGVyKHRlbnhfYW5ub3RhdGlvbiwgdHlwZSAlaW4lIGMoInFOU0MxIiwicU5TQzIiLCJhTlNDMCIsImFOU0MxIiwiYU5TQzIiKSwgYWdlID09ICJ5b3VuZyIpICwgYW5ub3RhdGlvbl9idWxrID0gZmlsdGVyKGJ1bGtfYW5ub3RhdGlvbiwgdHlwZSAlaW4lIGMoIk5TQyIpICwgYWdlID09ICJvbGQiKSwgcHNldWRvYnVsa19jZWxsdHlwZSA9ICJwc2V1ZG8tYnVsayBzYW1wbGVzOiBOU0MgKGxvZzIoVFBNKzEpKSIgLCBidWxrX2NlbGx0eXBlID0gIiBidWxrIHNhbXBsZXM6IE5TQyAobG9nMihleHByZXNzaW9uKzEpKSIgLCBtYWluID0gIk5TQzogWW91bmcgcHNldWRvLWJ1bGsgKDEwWCkgdnMgb2xkIGJ1bGsiICkKYGBgCgpgYGB7cn0KUHNldWRvYnVsa19idWxrX2NvbXBhcmUocHNldWRvYnVsa19kYXRhID0gdGVueF9kYXRhX2Vuc2VtYmwgLCBidWxrX2RhdGEgPSBidWxrX2RhdGEgLCBhbm5vdGF0aW9uX3BzZXVkb2J1bGsgPSBmaWx0ZXIodGVueF9hbm5vdGF0aW9uLCB0eXBlICVpbiUgYygicU5TQzEiLCJxTlNDMiIsImFOU0MwIiwiYU5TQzEiLCJhTlNDMiIpLCBhZ2UgPT0gInlvdW5nIikgLCBhbm5vdGF0aW9uX2J1bGsgPSBmaWx0ZXIoYnVsa19hbm5vdGF0aW9uLCB0eXBlICVpbiUgYygiTlNDIikgLCBhZ2UgPT0gInlvdW5nIiksIHBzZXVkb2J1bGtfY2VsbHR5cGUgPSAicHNldWRvLWJ1bGsgc2FtcGxlczogTlNDIChsb2cyKFRQTSsxKSkiICwgYnVsa19jZWxsdHlwZSA9ICIgYnVsayBzYW1wbGVzOiBOU0MgKGxvZzIoZXhwcmVzc2lvbisxKSkiICwgbWFpbiA9ICJZb3VuZyBOU0M6IHBzZXVkby1idWxrICgxMFgpIHZzIGJ1bGsiICkKYGBgCgo8aHI+CgojIyBQc2V1ZG9idWxrIE1BIFBsb3QKCmBgYHtyfQpQc2V1ZG9idWxrX2J1bGtfTUFQbG90IDwtIGZ1bmN0aW9uKCBwc2V1ZG9idWxrX2RhdGEgPSBOVUxMICwgYW5ub3RhdGlvbl9wc2V1ZG9idWxrID0gTlVMTCAgLCBtYWluID0gIiIgLCBsYWJlTF90b3BfZ2VuZXMgPSBGQUxTRSAsIGhpZ2hsaWdodF9nZW5lcyA9IGMoKSAsIG1heF9mb2xkQ2hhbmdlID0gMTAgICwgbWF4X2Jhc2VNZWFuID0gMTAwMDAwMCApewogIHJlcXVpcmUoImdncGxvdDIiKQogIHJlcXVpcmUoImRwbHlyIikKICByZXF1aXJlKCJ0aWJibGUiKQogIAogICMjIFN0cmlwIExvZzIgdHJhbnNmb3JtIG9mIHRoZSBkYXRhCiAgcHNldWRvYnVsa19kYXRhIDwtIDJeKHBzZXVkb2J1bGtfZGF0YSkgLSAxCiAgYnVsa19kYXRhIDwtIDJeKGJ1bGtfZGF0YSkgLSAxCgogIGlmKCBpcy5udWxsKHBzZXVkb2J1bGtfZGF0YSkgKXsgc3RvcCgiWW91IG5lZWQgdG8gc3VwcGx5IGFsbCBkYXRhICEiKSB9CiAgaWYoIGlzLm51bGwoYW5ub3RhdGlvbl9wc2V1ZG9idWxrKSApe3N0b3AoIllvdSBuZWVkIHRvIHN1cHBseSB0aGUgYW5ub3RhdGlvbiAhIil9CiAgCiAgYW5ub3RhdGlvbl9wc2V1ZG9idWxrX29sZCA8LSBmaWx0ZXIoYW5ub3RhdGlvbl9wc2V1ZG9idWxrICwgYWdlID09ICJvbGQiKQogIGFubm90YXRpb25fcHNldWRvYnVsa195b3VuZyA8LSBmaWx0ZXIoYW5ub3RhdGlvbl9wc2V1ZG9idWxrICwgYWdlID09ICJ5b3VuZyIpCgogIHBzZXVkb2J1bGtfZGF0YSA8LSBwc2V1ZG9idWxrX2RhdGFbIWlzLm5hKHJvd25hbWVzKHBzZXVkb2J1bGtfZGF0YSkpLF0KICAgIAogIHBzZXVkb2J1bGtfZGF0YV9vbGQgPC0gcHNldWRvYnVsa19kYXRhWyxhcy5jaGFyYWN0ZXIoYW5ub3RhdGlvbl9wc2V1ZG9idWxrX29sZCRjZWxsKV0KICBwc2V1ZG9idWxrX2RhdGFfeW91bmcgPC0gcHNldWRvYnVsa19kYXRhWyxhcy5jaGFyYWN0ZXIoYW5ub3RhdGlvbl9wc2V1ZG9idWxrX3lvdW5nJGNlbGwpXQogIAogIHBzZXVkb2J1bGtfZGF0YV9jb21wbGV0ZSA8LSBwc2V1ZG9idWxrX2RhdGFbLCBjKCBhcy5jaGFyYWN0ZXIoYW5ub3RhdGlvbl9wc2V1ZG9idWxrX29sZCRjZWxsKSxhcy5jaGFyYWN0ZXIoYW5ub3RhdGlvbl9wc2V1ZG9idWxrX3lvdW5nJGNlbGwpKSBdCiAgCiAgaWYoICEgYWxsKHJvd25hbWVzKHBzZXVkb2J1bGtfZGF0YV9vbGQpID09IHJvd25hbWVzKHBzZXVkb2J1bGtfZGF0YV95b3VuZykpKXtzdG9wKCJSb3duYW1lcyBhcmUgbm90IGNvcnJlY3Q6IFBzZXVkb2J1bGsiKX0KCiAgcHNldWRvYnVsa19kYXRhX2NvbWIgPC0gZGF0YS5mcmFtZSggbG9nMkZvbGRDaGFuZ2UgPSBsb2cyKCgocm93U3Vtcyhwc2V1ZG9idWxrX2RhdGFfb2xkKSArIDEpIC8gKHJvd1N1bXMocHNldWRvYnVsa19kYXRhX3lvdW5nKSArIDEpICkgKSAgLCBleHByX3lvdW5nX3BzZXVkb2J1bGsgPSByb3dTdW1zKHBzZXVkb2J1bGtfZGF0YV95b3VuZykgLCBiYXNlTWVhbiA9IHJvd01lYW5zKHBzZXVkb2J1bGtfZGF0YV9jb21wbGV0ZSksICBleHByX29sZF9wc2V1ZG9idWxrID0gcm93U3Vtcyhwc2V1ZG9idWxrX2RhdGFfb2xkKSAsIGdlbmUgPSByb3duYW1lcyhwc2V1ZG9idWxrX2RhdGFfb2xkKSApCiAgCiAgcHNldWRvYnVsa19kYXRhX2NvbWIkbG9nMkZvbGRDaGFuZ2VbaXMubmFuKHBzZXVkb2J1bGtfZGF0YV9jb21iJGxvZzJGb2xkQ2hhbmdlKV0gPC0gMAogIAogIHhfaGlnaGxpZ2h0IDwtIGZpbHRlcihwc2V1ZG9idWxrX2RhdGFfY29tYiAsZ2VuZSAlaW4lIGhpZ2hsaWdodF9nZW5lcyApCiAgCiAgZyA8LSBnZ3Bsb3QoZGF0YSA9IHBzZXVkb2J1bGtfZGF0YV9jb21iICwgbWFwcGluZyA9IGFlcyh4ID0gYmFzZU1lYW4gLCB5ID0gbG9nMkZvbGRDaGFuZ2UpKSArICBsYWJzKHggPSAiYmFzZU1lYW4iICAsIHkgPSAiTG9nMkZvbGRDaGFuZ2UiICkgKyBnZ3RpdGxlKG1haW4pICsgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCkgKyBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwKSArIGdlb21fcG9pbnQoY29sb3IgPSAiZ3JleSIgKSArIGdlb21fcG9pbnQoZGF0YSA9IHhfaGlnaGxpZ2h0ICwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImN5YW4iICAsIHNpemUgPSAyICwgcGNoID0gMjEpIAogIAogIGlmKCBsYWJlTF90b3BfZ2VuZXMgKXsKICAgICAgZyA8LSBnICsgZ2VvbV90ZXh0X3JlcGVsKGRhdGEgPSB4X2xhYmVsICwgbWFwcGluZyA9IGFlcyggeCA9IGJhc2VNZWFuICwgeSA9IGxvZzJGb2xkQ2hhbmdlICwgbGFiZWwgPSBnZW5lKSkgCiAgfQogIAogIGcgPC0gZyArIHNjYWxlX3hfbG9nMTAobGltaXRzID0gYygwLjAwMDAxLCBtYXhfYmFzZU1lYW4gKSwgZXhwYW5kID0gYygwLCAwKSkgKyBnZ3RpdGxlKG1haW4pICsgeWxpbShjKC1tYXhfZm9sZENoYW5nZSxtYXhfZm9sZENoYW5nZSkpCiAgCiAgZwogIH0KYGBgCgpgYGB7ciBmaWcud2lkdGg9MTUsIGZpZy5oZWlnaHQ9MTV9ClBzZXVkb2J1bGtfYnVsa19NQVBsb3QocHNldWRvYnVsa19kYXRhID0gc21hcnRzZXEyX2RhdGEgLCBhbm5vdGF0aW9uX3BzZXVkb2J1bGsgPSBzbWFydHNlcTJfYW5ub3RhdGlvbikKYGBgCgpgYGB7ciBmaWcud2lkdGg9MTUsIGZpZy5oZWlnaHQ9MTV9ClBzZXVkb2J1bGtfYnVsa19NQVBsb3QocHNldWRvYnVsa19kYXRhID0gdGVueF9kYXRhICwgYW5ub3RhdGlvbl9wc2V1ZG9idWxrID0gZHBseXI6OmZpbHRlcih0ZW54X2Fubm90YXRpb24sIHR5cGUgJWluJSBjKCJxTlNDMSIsInFOU0MyIiwiYU5TQzAiLCJhTlNDMSIsImFOU0MyIikpICkKYGBgCgoKIyBQbG90IGluZGl2aWR1YWwgZ2VuZXMgaW4gdGhlIHNpbmdsZSBjZWxsIGRhdGFzZXRzCgojIyBCZWVzd2FybSBwbG90IG9mIElsMzMKCiMjIyBTTUFSVHNlcTIKCmBgYHtyfQpJTDMzX3NtYXJ0c2VxMl9kYXRhIDwtIHNtYXJ0c2VxMl9kYXRhWyJFTlNNVVNHMDAwMDAwMjQ4MTAiLF0KYGBgCgpgYGB7cn0KSUwzM19zbWFydHNlcTJfZGYgPC0gZGF0YS5mcmFtZSggZXhwcmVzc2lvbiA9IElMMzNfc21hcnRzZXEyX2RhdGEgLCByb3cubmFtZXMgPSBuYW1lcyhJTDMzX3NtYXJ0c2VxMl9kYXRhKSAgKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJjZWxsIikgJT4lIGxlZnRfam9pbiggc21hcnRzZXEyX2Fubm90YXRpb24gLCBieSA9ICJjZWxsIikKYGBgCgpgYGB7cn0KYWRkbWFyZ2lucyggdGFibGUoIElMMzNfc21hcnRzZXEyX2RmJGFnZSAgLCBJTDMzX3NtYXJ0c2VxMl9kZiRleHByZXNzaW9uID4gMi41ICkgKQpgYGAKCmBgYHtyIGZpZy53aWR0aD05ICwgZmlnLmhlaWdodD01fQpsaWJyYXJ5KGdnYmVlc3dhcm0pCgpnZ3Bsb3QoZGF0YSA9IElMMzNfc21hcnRzZXEyX2RmICwgbWFwcGluZyA9IGFlcyh4ID0gYWdlICwgeSA9IGV4cHJlc3Npb24gLCBjb2xvciA9IHR5cGUgKSkgKyBnZW9tX3F1YXNpcmFuZG9tKCBzaXplID0gMS41ICxtZXRob2QgPSAic21pbGV5IiApICsgeGxhYigiQWdlIikgKyB5bGFiKCJsb2cyKFRQTSsxKSIpICsgZ2d0aXRsZSgiRXhwcmVzc2lvbiBvZiBJbDMzIGluIHRoZSBTTUFSVHNlcTIgZGF0YSIpICsgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBjKHlvdW5nID0gIjIgbW9udGgiICwgb2xkID0gIjIyIG1vbnRoIikgKSArIHNjYWxlX2NvbG9yX21hbnVhbCggbmFtZSA9ICJBY3RpdmF0aW9uIHN0YXRlIiAsIHZhbHVlcyA9IHNtYXJ0c2VxMl9jZWxsdHlwZV9jb2xvcnMgKSArIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXM9bGlzdChzaXplPTEuNSkpKQpgYGAKCgojIyMgMTBYCgpgYGB7cn0KSUwzM18xMFhfZGF0YSA8LSB0ZW54X2RhdGFfZW5zZW1ibFsiRU5TTVVTRzAwMDAwMDI0ODEwIixdCmBgYAoKYGBge3J9CnRlbnhfYW5ub3RhdGlvbl9uc2NzIDwtIHRlbnhfYW5ub3RhdGlvbiAlPiUgZmlsdGVyKCB0eXBlICVpbiUgYygicU5TQzEiLCJxTlNDMiIsImFOU0MwIiwiYU5TQzEiLCJhTlNDMiIpKQoKSUwzM18xMFhfZGYgPC0gZGF0YS5mcmFtZSggZXhwcmVzc2lvbiA9IElMMzNfMTBYX2RhdGEgLCByb3cubmFtZXMgPSBuYW1lcyhJTDMzXzEwWF9kYXRhKSAgKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJjZWxsIikgJT4lIGZpbHRlcihjZWxsICVpbiUgYXMuY2hhcmFjdGVyKCB0ZW54X2Fubm90YXRpb25fbnNjcyRjZWxsICkgKSAlPiUgbGVmdF9qb2luKCB0ZW54X2Fubm90YXRpb25fbnNjcyAsIGJ5ID0gImNlbGwiKQpgYGAKCmBgYHtyfQphZGRtYXJnaW5zKCB0YWJsZSggSUwzM18xMFhfZGYkYWdlICAsIElMMzNfMTBYX2RmJGV4cHJlc3Npb24gPiAwLjUgKSApCmBgYAoKYGBge3IgZmlnLndpZHRoPTkgLCBmaWcuaGVpZ2h0PTV9CmxpYnJhcnkoZ2diZWVzd2FybSkKCmdncGxvdChkYXRhID0gSUwzM18xMFhfZGYgLCBtYXBwaW5nID0gYWVzKHggPSBhZ2UgLCB5ID0gZXhwcmVzc2lvbiAsIGNvbG9yID0gdHlwZSApKSArIHhsYWIoIkFnZSIpICsgeWxhYigibG9nMihleHByZXNzaW9uKzEpIikgKyBnZ3RpdGxlKCJFeHByZXNzaW9uIG9mIElsMzMgaW4gdGhlIDEwWCBkYXRhIikgKyBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGMoeW91bmcgPSAiMiBtb250aCIgLCBvbGQgPSAiMjIgbW9udGgiKSApICsgc2NhbGVfY29sb3JfbWFudWFsKCBuYW1lID0gIkFjdGl2YXRpb24gc3RhdGUiICwgdmFsdWVzID0gdGVueF9jZWxsdHlwZV9jb2xvcnMgKSArIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXM9bGlzdChzaXplPTEuNSkpKSArIGdlb21fcXVhc2lyYW5kb20oIHNpemUgPSAwLjUgLG1ldGhvZCA9ICJzbWlsZXkiICkgCmBgYAoKPGhyPgoKIyBTZWFyY2ggZm9yIGdlbmVzIGZyb20gR2VuZSBMaXN0cyBpbiBhbGwgZGF0YXNldHMKCiMjIElubmF0ZURCIGdlbmVzIC0gbWFudWFsbHkgY3VyYXRlZCBmcm9tIHRoZSBidWxrIHNlcSBkYXRhIChmcm9tIHNpZy4gZ2VuZXMgd2l0aCBsb2cyRm9sZENoYW5nZSA+IDEpCgpgYGB7cn0KREVfZ2VuZXNfTlNDc19idWxrX3VwX0lubmF0ZURCIDwtIHJlYWQuY3N2KCJHZW5lTGlzdHMvTWFudWFsbHlfY3VyYXRlZF9ERV9nZW5lc19mcm9tX2J1bGsvSW5uYXRlREJfR2VuZV9saXN0L0lubmF0ZURCX05TQ3MuY3N2IiAsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSAsIGhlYWRlciA9IFRSVUUpCgpsaWJyYXJ5KGJpb21hUnQpCm1hcnQgPC0gdXNlTWFydChiaW9tYXJ0ID0gImVuc2VtYmwiLCBkYXRhc2V0ID0gIm1tdXNjdWx1c19nZW5lX2Vuc2VtYmwiKQpnZW5lc19zeW1ib2xfZW5zZW1ibCA8LSBnZXRCTShhdHRyaWJ1dGVzID0gYygibWdpX3N5bWJvbCIsImVuc2VtYmxfZ2VuZV9pZCIgKSwgZmlsdGVycyA9ICJtZ2lfc3ltYm9sIiAsIHZhbHVlcyA9IERFX2dlbmVzX05TQ3NfYnVsa191cF9Jbm5hdGVEQiRHZW5lcyAsIG1hcnQgPSBtYXJ0ICApCmdlbmVzX3N5bWJvbF9lbnNlbWJsIDwtIGRwbHlyOjpyZW5hbWUoZ2VuZXNfc3ltYm9sX2Vuc2VtYmwgLCBNR0kuc3ltYm9sID0gbWdpX3N5bWJvbCAsIGdlbmUgPSBlbnNlbWJsX2dlbmVfaWQgKQoKZ2VuZXNfc3ltYm9sX2Vuc2VtYmwgPC0gIGRwbHlyOjpmaWx0ZXIoIGdlbmVzX3N5bWJvbF9lbnNlbWJsICwgZ2VuZSAlaW4lIGFzLmNoYXJhY3RlcihidWxrX0RFX2dlbmVzX05TQyAlPiUgZmlsdGVyKHBhZGogPCAwLjA1ICwgbG9nMkZvbGRDaGFuZ2UgPiAxKSAlPiUgcHVsbCgiZW5zZW1ibF9nZW5lX2lkIikgKSApCgpERV9nZW5lc19OU0NzX2J1bGtfdXBfSW5uYXRlREIgPC0gZ2VuZXNfc3ltYm9sX2Vuc2VtYmwKYGBgCgpgYGB7cn0KZ2VuZV9zZXQgPC0gREVfZ2VuZXNfTlNDc19idWxrX3VwX0lubmF0ZURCCmBgYAoKYGBge3J9CnBsb3RfdGl0bGUgPC0gIk5TQyBidWxrIHNlcSAodG9wIERFIGdlbmVzIGJldHdlZW4gb2xkIGFuZCB5b3VuZykgYW5ub3RhdGVkIGluIElubmF0ZURCIgpgYGAKCgojIyMgQnVsayB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30KCiMjIyMgQnVsayBzZXF1ZW5jaW5nOiBIZWF0bWFwCgoKYGBge3IgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NX0KSGVhdG1hcEdlbmVzKHggPSBidWxrX2RhdGEgLCBnZW5lcyA9IHVuaXF1ZShnZW5lX3NldCRnZW5lKSAsIG1haW4gPSBwYXN0ZSgiQnVsazoiLCBwbG90X3RpdGxlKSAsIGFubm90YXRpb24gPSBidWxrX2Fubm90YXRpb24gLCBzaG93X2NvbG5hbWVzID0gRkFMU0UgLCBhbm5vdGF0aW9uX2NvbG9ycyA9IGxpc3QoYWdlID0gYWdlX2NvbG9yc19idWxrICkgKQpgYGAKCiMjIyMgQnVsayBzZXF1ZW5jaW5nOiBNQS1QbG90IHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQoKIyMjIyMgTlNDCgpgYGB7ciBmaWcud2lkdGg9MTV9Ck1BUGxvdCh4ID0gYnVsa19ERV9nZW5lc19OU0MgLCBtYWluID0gIk5TQyAoMTkgbW9udGggdnMgMiBtb250aCkiICwgaGlnaGxpZ2h0X2dlbmVzID0gdW5pcXVlKGdlbmVfc2V0JE1HSS5zeW1ib2wpICkgCmBgYAoKYGBge3IgZmlnLndpZHRoPTE1fQpNQVBsb3QoeCA9IGJ1bGtfREVfZ2VuZXNfTlNDX01vN192c195b3VuZyAsIG1haW4gPSAiTlNDICg3IE1vbnRoIHZzIDIgTW9udGgpIiAsIGhpZ2hsaWdodF9nZW5lcyA9IHVuaXF1ZShnZW5lX3NldCRNR0kuc3ltYm9sKSApIApgYGAKCiMjIyMjIE5CCgpgYGB7ciBmaWcud2lkdGg9MTV9Ck1BUGxvdCh4ID0gYnVsa19ERV9nZW5lc19OQiAsIG1haW4gPSAiTkIiICwgaGlnaGxpZ2h0X2dlbmVzID0gdW5pcXVlKGdlbmVfc2V0JE1HSS5zeW1ib2wpICkgCmBgYAoKIyMjIyMgTWljcm9nbGlhCgpgYGB7ciBmaWcud2lkdGg9MTV9Ck1BUGxvdCh4ID0gYnVsa19ERV9nZW5lc19NaWNyb2dsaWEgLCBtYWluID0gIk1pY3JvZ2xpYSIgLCBoaWdobGlnaHRfZ2VuZXMgPSB1bmlxdWUoZ2VuZV9zZXQkTUdJLnN5bWJvbCkgKSAKYGBgCgojIyMjIyBFbmRvdGhlbGlhbCBjZWxscwoKYGBge3IgZmlnLndpZHRoPTE1fQpNQVBsb3QoeCA9IGJ1bGtfREVfZ2VuZXNfRW5kb3RoZWxpYWwgLCBtYWluID0gIkVuZG90aGVsaWFsIiAsIGhpZ2hsaWdodF9nZW5lcyA9IHVuaXF1ZShnZW5lX3NldCRNR0kuc3ltYm9sKSApIApgYGAKCiMjIyMjIFBzZXVkb2J1bGsgIFNNQVJUc2VxMgoKYGBge3IgZmlnLndpZHRoPTE1LCBmaWcuaGVpZ2h0PTE1fQpQc2V1ZG9idWxrX2J1bGtfTUFQbG90KHBzZXVkb2J1bGtfZGF0YSA9IHNtYXJ0c2VxMl9kYXRhICwgYW5ub3RhdGlvbl9wc2V1ZG9idWxrID0gc21hcnRzZXEyX2Fubm90YXRpb24gLCBoaWdobGlnaHRfZ2VuZXMgPSB1bmlxdWUoZ2VuZV9zZXQkZ2VuZSkgLCBtYWluID0gIlBzZXVkb2J1bGs6IFNNQVJUc2VxMiIpCmBgYAoKIyMjIyMgUHNldWRvYnVsayAxMFgKCmBgYHtyIGZpZy53aWR0aD0xNSwgZmlnLmhlaWdodD0xNX0KUHNldWRvYnVsa19idWxrX01BUGxvdChwc2V1ZG9idWxrX2RhdGEgPSB0ZW54X2RhdGEgLCBhbm5vdGF0aW9uX3BzZXVkb2J1bGsgPSBkcGx5cjo6ZmlsdGVyKHRlbnhfYW5ub3RhdGlvbiwgdHlwZSAlaW4lIGMoInFOU0MxIiwicU5TQzIiLCJhTlNDMCIsImFOU0MxIiwiYU5TQzIiKSkgLCBoaWdobGlnaHRfZ2VuZXMgPSB1bmlxdWUoZ2VuZV9zZXQkTUdJLnN5bWJvbCkgLCBtYWluID0gIlBzZXVkb2J1bGs6IDEwWCIpCmBgYAoKCiMjIyBTTUFSVHNlcTIgey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9CgojIyMjIFNNQVJUc2VxMiBzZXF1ZW5jaW5nOiBIZWF0bWFwCgojIyMjIyBBcnJhbmdlZCBieSBjZWxsdHlwZSB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30KCmBgYHtyfQogSGVhdG1hcEdlbmVzKHggPSBzbWFydHNlcTJfZGF0YSAsIGdlbmVzID0gdW5pcXVlKGdlbmVfc2V0JGdlbmUpICwgbWFpbiA9IHBhc3RlKCJTTUFSVHNlcTI6IiwgcGxvdF90aXRsZSkgLCBhbm5vdGF0aW9uID0gc21hcnRzZXEyX2Fubm90YXRpb24gLCBhbm5vdGF0aW9uX2NvbG9ycyA9IGxpc3QoYWdlID0gYWdlX2NvbG9ycyAsIHR5cGUgPSBzbWFydHNlcTJfY2VsbHR5cGVfY29sb3JzICkgICkKYGBgCgojIyMjIyBDb2x1bW5zIGNsdXN0ZXJlZAoKYGBge3IgZmlnLndpZHRoPTEwLjEgLCBmaWcuaGVpZ2h0PTEwLjJ9CiB0ZXN0IDwtIEhlYXRtYXBHZW5lcyh4ID0gc21hcnRzZXEyX2RhdGEgLCBnZW5lcyA9IHVuaXF1ZShnZW5lX3NldCRnZW5lKSAsIG1haW4gPSBwYXN0ZSgiU01BUlRzZXEyOiIsIHBsb3RfdGl0bGUpICwgYW5ub3RhdGlvbiA9IHNtYXJ0c2VxMl9hbm5vdGF0aW9uICU+JSBkcGx5cjo6ZmlsdGVyKHR5cGUgJWluJSBjKCJxTlNDMSIsInFOU0MyIikpICwgYW5ub3RhdGlvbl9jb2xvcnMgPSBsaXN0KGFnZSA9IGFnZV9jb2xvcnMgLCB0eXBlID0gc21hcnRzZXEyX2NlbGx0eXBlX2NvbG9ycyApICwgY2x1c3Rlcl9jb2xzID0gVFJVRSApCmBgYAoKCgojIyMgMTBYIHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQoKIyMjIyAxMFggc2VxdWVuY2luZzogSGVhdG1hcAoKYGBge3IgZmlnLndpZHRoPTEwLjEsIGZpZy5oZWlnaHQ9MTAuMn0KSGVhdG1hcEdlbmVzKHggPSB0ZW54X2RhdGFfZW5zZW1ibCAsIGdlbmVzID0gdW5pcXVlKGdlbmVfc2V0JGdlbmUpICwgbWFpbiA9IHBhc3RlKCIxMFg6IiwgcGxvdF90aXRsZSkgLCBhbm5vdGF0aW9uID0gdGVueF9hbm5vdGF0aW9uICwgYW5ub3RhdGlvbl9jb2xvcnMgPSBsaXN0KGFnZSA9IGFnZV9jb2xvcnMgLCB0eXBlID0gdGVueF9jZWxsdHlwZV9jb2xvcnMgKSApCmBgYAoKYGBge3IgZmlnLndpZHRoPTEwLjEsIGZpZy5oZWlnaHQ9MTAuMn0KSGVhdG1hcEdlbmVzKHggPSB0ZW54X2RhdGFfZW5zZW1ibCAsIGdlbmVzID0gdW5pcXVlKGdlbmVfc2V0JGdlbmUpICwgbWFpbiA9IHBhc3RlKCIxMFg6IiwgcGxvdF90aXRsZSkgLCBhbm5vdGF0aW9uID0gdGVueF9hbm5vdGF0aW9uICwgYW5ub3RhdGlvbl9jb2xvcnMgPSBsaXN0KGFnZSA9IGFnZV9jb2xvcnMgLCB0eXBlID0gdGVueF9jZWxsdHlwZV9jb2xvcnMgKSAsIGNsdXN0ZXJfY29scyA9IFRSVUUgKQpgYGAKCgojIyAgSW5uYXRlREIgZ2VuZXMgLSBtYW51YWxseSBjdXJhdGVkIGZyb20gdGhlIGJ1bGsgc2VxIGRhdGEgKGZyb20gc2lnLiBnZW5lcyB3aXRoIGxvZzJGb2xkQ2hhbmdlID4gMSkgLSBmcm9tIEVuZG90aGVsaWFsLCBOQiBhbmQgTWljcm9nbGlhCgojIyMgRW5kb3RoZWxpYWwKCmBgYHtyfQpERV9nZW5lc19FbmRvdGhlbGlhbF9idWxrX3VwX0lubmF0ZURCIDwtIHJlYWQuY3N2KCJHZW5lTGlzdHMvTWFudWFsbHlfY3VyYXRlZF9ERV9nZW5lc19mcm9tX2J1bGsvSW5uYXRlREJfR2VuZV9saXN0L0lubmF0ZURCX0VuZG9zLmNzdiIgLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UgLCBoZWFkZXIgPSBUUlVFKQoKbGlicmFyeShiaW9tYVJ0KQptYXJ0IDwtIHVzZU1hcnQoYmlvbWFydCA9ICJlbnNlbWJsIiwgZGF0YXNldCA9ICJtbXVzY3VsdXNfZ2VuZV9lbnNlbWJsIikKZ2VuZXNfc3ltYm9sX2Vuc2VtYmwgPC0gZ2V0Qk0oYXR0cmlidXRlcyA9IGMoIm1naV9zeW1ib2wiLCJlbnNlbWJsX2dlbmVfaWQiICksIGZpbHRlcnMgPSAibWdpX3N5bWJvbCIgLCB2YWx1ZXMgPSBERV9nZW5lc19FbmRvdGhlbGlhbF9idWxrX3VwX0lubmF0ZURCJEdlbmVzICwgbWFydCA9IG1hcnQgICkKZ2VuZXNfc3ltYm9sX2Vuc2VtYmwgPC0gZHBseXI6OnJlbmFtZShnZW5lc19zeW1ib2xfZW5zZW1ibCAsIE1HSS5zeW1ib2wgPSBtZ2lfc3ltYm9sICwgZ2VuZSA9IGVuc2VtYmxfZ2VuZV9pZCApCgpnZW5lc19zeW1ib2xfZW5zZW1ibCA8LSAgZHBseXI6OmZpbHRlciggZ2VuZXNfc3ltYm9sX2Vuc2VtYmwgLCBnZW5lICVpbiUgYXMuY2hhcmFjdGVyKGJ1bGtfREVfZ2VuZXNfRW5kb3RoZWxpYWwgJT4lIGZpbHRlcihwYWRqIDwgMC4wNSAsIGxvZzJGb2xkQ2hhbmdlID4gMSkgJT4lIHB1bGwoImVuc2VtYmxfZ2VuZV9pZCIpICkgKQoKREVfZ2VuZXNfRW5kb3RoZWxpYWxfYnVsa191cF9Jbm5hdGVEQiA8LSBnZW5lc19zeW1ib2xfZW5zZW1ibApgYGAKCmBgYHtyIGZpZy53aWR0aD0xNX0KTUFQbG90KHggPSBidWxrX0RFX2dlbmVzX0VuZG90aGVsaWFsICwgbWFpbiA9ICJFbmRvdGhlbGlhbCIgLCBoaWdobGlnaHRfZ2VuZXMgPSB1bmlxdWUoREVfZ2VuZXNfRW5kb3RoZWxpYWxfYnVsa191cF9Jbm5hdGVEQiRNR0kuc3ltYm9sKSApIApgYGAKCiMjIyBNaWNyb2dsaWEKCmBgYHtyfQpERV9nZW5lc19NaWNyb2dsaWFfYnVsa191cF9Jbm5hdGVEQiA8LSByZWFkLmNzdigiR2VuZUxpc3RzL01hbnVhbGx5X2N1cmF0ZWRfREVfZ2VuZXNfZnJvbV9idWxrL0lubmF0ZURCX0dlbmVfbGlzdC9Jbm5hdGVEQl9NaWNyb2dsaWEuY3N2IiAsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSAsIGhlYWRlciA9IFRSVUUpCgpsaWJyYXJ5KGJpb21hUnQpCm1hcnQgPC0gdXNlTWFydChiaW9tYXJ0ID0gImVuc2VtYmwiLCBkYXRhc2V0ID0gIm1tdXNjdWx1c19nZW5lX2Vuc2VtYmwiKQpnZW5lc19zeW1ib2xfZW5zZW1ibCA8LSBnZXRCTShhdHRyaWJ1dGVzID0gYygibWdpX3N5bWJvbCIsImVuc2VtYmxfZ2VuZV9pZCIgKSwgZmlsdGVycyA9ICJtZ2lfc3ltYm9sIiAsIHZhbHVlcyA9IERFX2dlbmVzX01pY3JvZ2xpYV9idWxrX3VwX0lubmF0ZURCJEdlbmVzICwgbWFydCA9IG1hcnQgICkKZ2VuZXNfc3ltYm9sX2Vuc2VtYmwgPC0gZHBseXI6OnJlbmFtZShnZW5lc19zeW1ib2xfZW5zZW1ibCAsIE1HSS5zeW1ib2wgPSBtZ2lfc3ltYm9sICwgZ2VuZSA9IGVuc2VtYmxfZ2VuZV9pZCApCgpnZW5lc19zeW1ib2xfZW5zZW1ibCA8LSAgZHBseXI6OmZpbHRlciggZ2VuZXNfc3ltYm9sX2Vuc2VtYmwgLCBnZW5lICVpbiUgYXMuY2hhcmFjdGVyKGJ1bGtfREVfZ2VuZXNfTWljcm9nbGlhICU+JSBkcGx5cjo6ZmlsdGVyKHBhZGogPCAwLjA1ICwgbG9nMkZvbGRDaGFuZ2UgPiAxKSAlPiUgcHVsbCgiZW5zZW1ibF9nZW5lX2lkIikgKSApCgpERV9nZW5lc19NaWNyb2dsaWFfYnVsa191cF9Jbm5hdGVEQiA8LSBnZW5lc19zeW1ib2xfZW5zZW1ibApgYGAKCmBgYHtyIGZpZy53aWR0aD0xNX0KTUFQbG90KHggPSBidWxrX0RFX2dlbmVzX01pY3JvZ2xpYSAsIG1haW4gPSAiTWljcm9nbGlhIiAsIGhpZ2hsaWdodF9nZW5lcyA9IHVuaXF1ZShERV9nZW5lc19NaWNyb2dsaWFfYnVsa191cF9Jbm5hdGVEQiRNR0kuc3ltYm9sKSApIApgYGAKCiMjIyBOQgoKYGBge3J9CkRFX2dlbmVzX05CX2J1bGtfdXBfSW5uYXRlREIgPC0gcmVhZC5jc3YoIkdlbmVMaXN0cy9NYW51YWxseV9jdXJhdGVkX0RFX2dlbmVzX2Zyb21fYnVsay9Jbm5hdGVEQl9HZW5lX2xpc3QvSW5uYXRlREJfTmV1cm9ibGFzdHMuY3N2IiAsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSAsIGhlYWRlciA9IFRSVUUpCgpsaWJyYXJ5KGJpb21hUnQpCm1hcnQgPC0gdXNlTWFydChiaW9tYXJ0ID0gImVuc2VtYmwiLCBkYXRhc2V0ID0gIm1tdXNjdWx1c19nZW5lX2Vuc2VtYmwiKQpnZW5lc19zeW1ib2xfZW5zZW1ibCA8LSBnZXRCTShhdHRyaWJ1dGVzID0gYygibWdpX3N5bWJvbCIsImVuc2VtYmxfZ2VuZV9pZCIgKSwgZmlsdGVycyA9ICJtZ2lfc3ltYm9sIiAsIHZhbHVlcyA9IERFX2dlbmVzX05CX2J1bGtfdXBfSW5uYXRlREIkR2VuZXMgLCBtYXJ0ID0gbWFydCAgKQpnZW5lc19zeW1ib2xfZW5zZW1ibCA8LSBkcGx5cjo6cmVuYW1lKGdlbmVzX3N5bWJvbF9lbnNlbWJsICwgTUdJLnN5bWJvbCA9IG1naV9zeW1ib2wgLCBnZW5lID0gZW5zZW1ibF9nZW5lX2lkICkKCmdlbmVzX3N5bWJvbF9lbnNlbWJsIDwtICBkcGx5cjo6ZmlsdGVyKCBnZW5lc19zeW1ib2xfZW5zZW1ibCAsIGdlbmUgJWluJSBhcy5jaGFyYWN0ZXIoYnVsa19ERV9nZW5lc19OQiAlPiUgZHBseXI6OmZpbHRlcihwYWRqIDwgMC4wNSAsIGxvZzJGb2xkQ2hhbmdlID4gMSkgJT4lIHB1bGwoImVuc2VtYmxfZ2VuZV9pZCIpICkgKQoKREVfZ2VuZXNfTkJfYnVsa191cF9Jbm5hdGVEQiA8LSBnZW5lc19zeW1ib2xfZW5zZW1ibApgYGAKCmBgYHtyIGZpZy53aWR0aD0xNX0KTUFQbG90KHggPSBidWxrX0RFX2dlbmVzX05CICwgbWFpbiA9ICJOZXVyb2JsYXN0IiAsIGhpZ2hsaWdodF9nZW5lcyA9IHVuaXF1ZShERV9nZW5lc19OQl9idWxrX3VwX0lubmF0ZURCJE1HSS5zeW1ib2wpICkgCmBgYAoKIyBMb2FkIHJhdyBjb3VudHMKCldlIGNhbiBhbHNvIGNvbXBhcmUgdGhlIHJhdyBjb3VudHMgZm9yIHNwZWNpZmljIGdlbmVzIHRoYXQgd2UgcmVjb3ZlciB3aXRoIHRoZSBkaWZmZXJlbnQgc2VxdWVuY2luZyBtZXRob2RzCgojIyBCdWxrIHNlcXVlbmNpbmcgZGF0YQoKYGBge3J9CmRhdGFfYnVsayA8LSByZWFkLmNzdihmaWxlID0gInJhd19jb3VudHMvY29tcGF0aWJsZV9yb3duYW1lc19vbmx5X05TQ3MvQnVsa19zZXFfQWxsLmdlbmVzLmNvdW50c19jb21wYXQuY3N2IiAsIHJvdy5uYW1lcyA9IDEpCgphbm5vX2J1bGsgPC0gcmVhZC5jc3YoZmlsZSA9ICJyYXdfY291bnRzL2Fubm90YXRpb25fYnVsay5jc3YiICwgcm93Lm5hbWVzID0gMSkKYGBgCgpDaGVjayBpZiBhbGwgY2VsbHMgZnJvbSB0aGUgYW5ub3RhdGlvbiBhcmUgcHJlc2VudCBpbiB0aGUgZGF0YSBhcyBjb2x1bW5zIGFuZCB0aGUgb3RoZXIgd2F5IGFyb3VuZAoKYGBge3J9CmFsbChjb2xuYW1lcyhkYXRhX2J1bGspID09IGFubm9fYnVsayRuYW1lKQpgYGAKCgojIyBTTUFSVHNlcTIgZGF0YQoKYGBge3J9CmRhdGFfU01BUlRzZXEyX05TQ3MgPC0gcmVhZC5jc3YoZmlsZSA9ICJyYXdfY291bnRzL2NvbXBhdGlibGVfcm93bmFtZXNfb25seV9OU0NzL1NNQVJUc2VxMl9BbGwuZ2VuZXMuY291bnRzX2NvbXBhdC5jc3YiICwgcm93Lm5hbWVzID0gMSkKCmFubm9fU01BUlRzZXEyX05TQ3MgPC0gcmVhZC5jc3YoZmlsZSA9ICJyYXdfY291bnRzL2Fubm90YXRpb25fTlNDc19TTUFSVHNlcTIuY3N2IiAsIHJvdy5uYW1lcyA9IDEpCmBgYAoKQ2hlY2sgaWYgYWxsIGNlbGxzIGZyb20gdGhlIGFubm90YXRpb24gYXJlIHByZXNlbnQgaW4gdGhlIGRhdGEgYXMgY29sdW1ucyBhbmQgdGhlIG90aGVyIHdheSBhcm91bmQKCmBgYHtyfQphbGwoY29sbmFtZXMoZGF0YV9TTUFSVHNlcTJfTlNDcykgPT0gYW5ub19TTUFSVHNlcTJfTlNDcyRuYW1lKQpgYGAKCiMjIDEwWCBkYXRhCgpgYGB7cn0KZGF0YV8xMFhfTlNDcyA8LSByZWFkLmNzdihmaWxlID0gInJhd19jb3VudHMvY29tcGF0aWJsZV9yb3duYW1lc19vbmx5X05TQ3MvMTBYX0FsbC5nZW5lcy5jb3VudHNfY29tcGF0LmNzdiIgLCByb3cubmFtZXMgPSAxKQoKYW5ub18xMFhfTlNDcyA8LSByZWFkLmNzdihmaWxlID0gInJhd19jb3VudHMvYW5ub3RhdGlvbl8xMHhfTlNDcy5jc3YiICwgcm93Lm5hbWVzID0gMSkKYGBgCgpgYGB7cn0KY29sbmFtZXMoZGF0YV8xMFhfTlNDcykgPC0gc3ViKHggPSBjb2xuYW1lcyhkYXRhXzEwWF9OU0NzKSAsIHBhdHRlcm4gPSAiXFwuIiAsIHJlcGxhY2VtZW50ID0gIi0iICkKYGBgCgoKQ2hlY2sgaWYgYWxsIGNlbGxzIGZyb20gdGhlIGFubm90YXRpb24gYXJlIHByZXNlbnQgaW4gdGhlIGRhdGEgYXMgY29sdW1ucyBhbmQgdGhlIG90aGVyIHdheSBhcm91bmQKCmBgYHtyfQphbGwoY29sbmFtZXMoZGF0YV8xMFhfTlNDcykgPT0gYW5ub18xMFhfTlNDcyRuYW1lKQpgYGAKCiMgQ2hlY2sgcmF3IGNvdW50cyBmb3IgaGlnaGx5IHVwcmVndWxhdGVkIGdlbmVzIGluIGJ1bGsgTlNDIG9sZCB0byB5b3VuZyBjb21wYXJpc29uCgojIyAxMFgKCkhvdyBtYW55IGNvdW50cyBkbyBnZW5lcyBoYXZlIGluIHRoZSBTaW5nbGUgY2VsbCBkYXRhIHNldHMsIHRoYXQgYXJlIHNpZ25pZmljYW50bHkgdXByZWd1bGF0ZWQgaW4gdGhlIGJ1bGsgTlNDcwoKYGBge3J9CmNlbGxzX05TQ3NfMTBYIDwtIGFzLmNoYXJhY3Rlcihhbm5vXzEwWF9OU0NzJG5hbWUpCmBgYAoKCiMjIyBJaWdwMSAtIEVOU01VU0cwMDAwMDA1NDA3MgoKYGBge3J9CklpZ3AxX2RmIDwtIGRhdGEuZnJhbWUoIHRvdGFsUmVhZHMgPSBjb2xTdW1zKGRhdGFfMTBYX05TQ3MpICwgZ2VuZVJlYWRzID0gYXMubnVtZXJpYyhkYXRhXzEwWF9OU0NzWyJFTlNNVVNHMDAwMDAwNTQwNzIiLGNlbGxzX05TQ3NfMTBYXSkgLCBnZW5lID0gIklpZ3AxIiAgICkgICU+JSByb3duYW1lc190b19jb2x1bW4oIm5hbWUiKSAlPiUgbGVmdF9qb2luKGFubm9fMTBYX05TQ3MgLCBieSA9ICJuYW1lIiApCmBgYAoKYGBge3J9CmFkZG1hcmdpbnMoIHRhYmxlKCBJaWdwMV9kZiRhZ2UgICwgSWlncDFfZGYkZ2VuZVJlYWRzID4gMCApICkKYGBgCgpgYGB7ciBmaWcud2lkdGg9NiAsIGZpZy5oZWlnaHQ9Mn0KZ2dfSWlncDEgPC0gZ2dwbG90KGRhdGEgPSBJaWdwMV9kZiAsIG1hcHBpbmcgPSBhZXMoeCA9IHRvdGFsUmVhZHMgLCB5ID0gZ2VuZVJlYWRzICkgKSArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gYWdlKSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygib2xkIiA9ICJzbGF0ZWJsdWUiICwgInlvdW5nIiA9ICJ5ZWxsb3dncmVlbiIpICkKCmdnX0lpZ3AxCmBgYAoKIyMjIERkeDYwIC0gRU5TTVVTRzAwMDAwMDM3OTIxCgpgYGB7cn0KRGR4NjBfZGYgPC0gZGF0YS5mcmFtZSggdG90YWxSZWFkcyA9IGNvbFN1bXMoZGF0YV8xMFhfTlNDcykgLCBnZW5lUmVhZHMgPSBhcy5udW1lcmljKGRhdGFfMTBYX05TQ3NbIkVOU01VU0cwMDAwMDAzNzkyMSIsY2VsbHNfTlNDc18xMFhdKSAsIGdlbmUgPSAiRGR4NjAiICAgKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJuYW1lIikgJT4lIGxlZnRfam9pbihhbm5vXzEwWF9OU0NzICwgYnkgPSAibmFtZSIgKQpgYGAKCmBgYHtyfQphZGRtYXJnaW5zKCB0YWJsZSggRGR4NjBfZGYkYWdlICAsIERkeDYwX2RmJGdlbmVSZWFkcyA+IDAgKSApCmBgYAoKYGBge3IgZmlnLndpZHRoPTYgLCBmaWcuaGVpZ2h0PTJ9CmdnX0RkeDYwIDwtIGdncGxvdChkYXRhID0gRGR4NjBfZGYgLCBtYXBwaW5nID0gYWVzKHggPSB0b3RhbFJlYWRzICwgeSA9IGdlbmVSZWFkcyApICkgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGFnZSkpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIm9sZCIgPSAic2xhdGVibHVlIiAsICJ5b3VuZyIgPSAieWVsbG93Z3JlZW4iKSApCmBgYAoKIyMjIElmaXQxIC0gRU5TTVVTRzAwMDAwMDc4OTIwCgpgYGB7cn0KSWZpdDFfZGYgPC0gZGF0YS5mcmFtZSggdG90YWxSZWFkcyA9IGNvbFN1bXMoZGF0YV8xMFhfTlNDcykgLCBnZW5lUmVhZHMgPSBhcy5udW1lcmljKGRhdGFfMTBYX05TQ3NbIkVOU01VU0cwMDAwMDA3ODkyMCIsY2VsbHNfTlNDc18xMFhdKSAsIGdlbmUgPSAiSWZpdDEiICkgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigibmFtZSIpICU+JSBsZWZ0X2pvaW4oYW5ub18xMFhfTlNDcyAsIGJ5ID0gIm5hbWUiICkKYGBgCgpgYGB7cn0KYWRkbWFyZ2lucyggdGFibGUoIElmaXQxX2RmJGFnZSAgLCBJZml0MV9kZiRnZW5lUmVhZHMgPiAwICkgKQpgYGAKCmBgYHtyIGZpZy53aWR0aD02ICwgZmlnLmhlaWdodD0yfQpnZ19JZml0MSA8LSBnZ3Bsb3QoZGF0YSA9IElmaXQxX2RmICwgbWFwcGluZyA9IGFlcyh4ID0gdG90YWxSZWFkcyAsIHkgPSBnZW5lUmVhZHMgKSApICsgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBhZ2UpKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJvbGQiID0gInNsYXRlYmx1ZSIgLCAieW91bmciID0gInllbGxvd2dyZWVuIikgKQoKZ2dfSWZpdDEKYGBgCgojIyMgUnNhZDIgLSBFTlNNVVNHMDAwMDAwMjA2NDEKCmBgYHtyfQpSc2FkMl9kZiA8LSBkYXRhLmZyYW1lKCB0b3RhbFJlYWRzID0gY29sU3VtcyhkYXRhXzEwWF9OU0NzKSAsIGdlbmVSZWFkcyA9IGFzLm51bWVyaWMoZGF0YV8xMFhfTlNDc1siRU5TTVVTRzAwMDAwMDIwNjQxIixjZWxsc19OU0NzXzEwWF0pICwgZ2VuZSA9ICJSc2FkMiIgICkgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigibmFtZSIpICU+JSBsZWZ0X2pvaW4oYW5ub18xMFhfTlNDcyAsIGJ5ID0gIm5hbWUiICkKYGBgCgpgYGB7cn0KYWRkbWFyZ2lucyggdGFibGUoIFJzYWQyX2RmJGFnZSAgLCBSc2FkMl9kZiRnZW5lUmVhZHMgPiAwICkgKQpgYGAKCmBgYHtyIGZpZy53aWR0aD02ICwgZmlnLmhlaWdodD0yfQpnZ19Sc2FkMiA8LSBnZ3Bsb3QoZGF0YSA9IFJzYWQyX2RmICwgbWFwcGluZyA9IGFlcyh4ID0gdG90YWxSZWFkcyAsIHkgPSBnZW5lUmVhZHMgKSApICsgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBhZ2UpKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJvbGQiID0gInNsYXRlYmx1ZSIgLCAieW91bmciID0gInllbGxvd2dyZWVuIikgKQoKZ2dfUnNhZDIKYGBgCgojIyMgQzMgLSBFTlNNVVNHMDAwMDAwMjQxNjQKCmBgYHtyfQpDM19kZiA8LSBkYXRhLmZyYW1lKCB0b3RhbFJlYWRzID0gY29sU3VtcyhkYXRhXzEwWF9OU0NzKSAsIGdlbmVSZWFkcyA9IGFzLm51bWVyaWMoZGF0YV8xMFhfTlNDc1siRU5TTVVTRzAwMDAwMDI0MTY0IixjZWxsc19OU0NzXzEwWF0pICwgZ2VuZSA9ICJDMyIgICApICAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJuYW1lIikgJT4lIGxlZnRfam9pbihhbm5vXzEwWF9OU0NzICwgYnkgPSAibmFtZSIgKQpgYGAKCmBgYHtyfQphZGRtYXJnaW5zKCB0YWJsZSggQzNfZGYkYWdlICAsIEMzX2RmJGdlbmVSZWFkcyA+IDAgKSApCmBgYAoKYGBge3IgZmlnLndpZHRoPTYgLCBmaWcuaGVpZ2h0PTJ9CmdnX0MzIDwtIGdncGxvdChkYXRhID0gQzNfZGYgLCBtYXBwaW5nID0gYWVzKHggPSB0b3RhbFJlYWRzICwgeSA9IGdlbmVSZWFkcyApICkgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGFnZSkpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIm9sZCIgPSAic2xhdGVibHVlIiAsICJ5b3VuZyIgPSAieWVsbG93Z3JlZW4iKSApCgpnZ19DMwpgYGAKCgoKIyMjIElmaTQ0IC0gRU5TTVVTRzAwMDAwMDI4MDM3CgpgYGB7cn0KSWZpNDRfZGYgPC0gZGF0YS5mcmFtZSggdG90YWxSZWFkcyA9IGNvbFN1bXMoZGF0YV8xMFhfTlNDcykgLCBnZW5lUmVhZHMgPSBhcy5udW1lcmljKGRhdGFfMTBYX05TQ3NbIkVOU01VU0cwMDAwMDAyODAzNyIsY2VsbHNfTlNDc18xMFhdKSAsIGdlbmUgPSAiSWZpNDQiICAgKSAgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigibmFtZSIpICU+JSBsZWZ0X2pvaW4oYW5ub18xMFhfTlNDcyAsIGJ5ID0gIm5hbWUiICkKYGBgCgpgYGB7cn0KYWRkbWFyZ2lucyggdGFibGUoIElmaTQ0X2RmJGFnZSAgLCBJZmk0NF9kZiRnZW5lUmVhZHMgPiAwICkgKQpgYGAKCmBgYHtyIGZpZy53aWR0aD02ICwgZmlnLmhlaWdodD0yfQpnZ19JZmk0NCA8LSBnZ3Bsb3QoZGF0YSA9IElmaTQ0X2RmICwgbWFwcGluZyA9IGFlcyh4ID0gdG90YWxSZWFkcyAsIHkgPSBnZW5lUmVhZHMgKSApICsgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBhZ2UpKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJvbGQiID0gInNsYXRlYmx1ZSIgLCAieW91bmciID0gInllbGxvd2dyZWVuIikgKQoKZ2dfSWZpNDQKYGBgCgoKIyMjIEN4Y2wxMCAtIEVOU01VU0cwMDAwMDAzNDg1NQoKYGBge3J9CkN4Y2wxMF9kZiA8LSBkYXRhLmZyYW1lKCB0b3RhbFJlYWRzID0gY29sU3VtcyhkYXRhXzEwWF9OU0NzKSAsIGdlbmVSZWFkcyA9IGFzLm51bWVyaWMoZGF0YV8xMFhfTlNDc1siRU5TTVVTRzAwMDAwMDM0ODU1IixjZWxsc19OU0NzXzEwWF0pICwgZ2VuZSA9ICJDeGNsMTAiICAgKSAgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigibmFtZSIpICU+JSBsZWZ0X2pvaW4oYW5ub18xMFhfTlNDcyAsIGJ5ID0gIm5hbWUiICkKYGBgCmBgYHtyfQphZGRtYXJnaW5zKCB0YWJsZSggQ3hjbDEwX2RmJGFnZSAgLCBDeGNsMTBfZGYkZ2VuZVJlYWRzID4gMCApICkKYGBgCgpgYGB7ciBmaWcud2lkdGg9NiAsIGZpZy5oZWlnaHQ9Mn0KZ2dfQ3hjbDEwIDwtIGdncGxvdChkYXRhID0gQ3hjbDEwX2RmICwgbWFwcGluZyA9IGFlcyh4ID0gdG90YWxSZWFkcyAsIHkgPSBnZW5lUmVhZHMgKSApICsgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBhZ2UpKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJvbGQiID0gInNsYXRlYmx1ZSIgLCAieW91bmciID0gInllbGxvd2dyZWVuIikgKQoKZ2dfQ3hjbDEwCmBgYAoKIyMjIElsMzMgLSBFTlNNVVNHMDAwMDAwMjQ4MTAKCmBgYHtyfQpJbDMzX2RmIDwtIGRhdGEuZnJhbWUoIHRvdGFsUmVhZHMgPSBjb2xTdW1zKGRhdGFfMTBYX05TQ3MpICwgZ2VuZVJlYWRzID0gYXMubnVtZXJpYyhkYXRhXzEwWF9OU0NzWyJFTlNNVVNHMDAwMDAwMjQ4MTAiLGNlbGxzX05TQ3NfMTBYXSkgLCBnZW5lID0gIklsMzMiICAgKSAgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigibmFtZSIpICU+JSBsZWZ0X2pvaW4oYW5ub18xMFhfTlNDcyAsIGJ5ID0gIm5hbWUiICkKYGBgCgpgYGB7cn0KYWRkbWFyZ2lucyggdGFibGUoIElsMzNfZGYkYWdlICAsIElsMzNfZGYkZ2VuZVJlYWRzID4gMCApICkKYGBgCgpgYGB7ciBmaWcud2lkdGg9NiAsIGZpZy5oZWlnaHQ9Mn0KZ2dfSWwzMyA8LSBnZ3Bsb3QoZGF0YSA9IElsMzNfZGYgLCBtYXBwaW5nID0gYWVzKHggPSB0b3RhbFJlYWRzICwgeSA9IGdlbmVSZWFkcyApICkgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGFnZSkpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIm9sZCIgPSAic2xhdGVibHVlIiAsICJ5b3VuZyIgPSAieWVsbG93Z3JlZW4iKSApCgpnZ19JbDMzCmBgYAoKCgojIyMgSWZpNDcgLSBFTlNNVVNHMDAwMDAwNzg5MjAKCmBgYHtyfQpJZmk0N19kZiA8LSBkYXRhLmZyYW1lKCB0b3RhbFJlYWRzID0gY29sU3VtcyhkYXRhXzEwWF9OU0NzKSAsIGdlbmVSZWFkcyA9IGFzLm51bWVyaWMoZGF0YV8xMFhfTlNDc1siRU5TTVVTRzAwMDAwMDc4OTIwIixjZWxsc19OU0NzXzEwWF0pICwgZ2VuZSA9ICJJZmk0NyIgICApICU+JSByb3duYW1lc190b19jb2x1bW4oIm5hbWUiKSAlPiUgbGVmdF9qb2luKGFubm9fMTBYX05TQ3MgLCBieSA9ICJuYW1lIiApCmBgYAoKYGBge3J9CmFkZG1hcmdpbnMoIHRhYmxlKCBJZmk0N19kZiRhZ2UgICwgSWZpNDdfZGYkZ2VuZVJlYWRzID4gMCApICkKYGBgCgpgYGB7ciBmaWcud2lkdGg9NiAsIGZpZy5oZWlnaHQ9Mn0KZ2dfSWZpNDcgPC0gZ2dwbG90KGRhdGEgPSBJZmk0N19kZiAsIG1hcHBpbmcgPSBhZXMoeCA9IHRvdGFsUmVhZHMgLCB5ID0gZ2VuZVJlYWRzICkgKSArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gYWdlKSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygib2xkIiA9ICJzbGF0ZWJsdWUiICwgInlvdW5nIiA9ICJ5ZWxsb3dncmVlbiIpICkKCmdnX0lmaTQ3CmBgYAoKIyMjIEdtNDk1MSAtIEVOU01VU0cwMDAwMDA3MzU1NQoKYGBge3J9CkdtNDk1MV9kZiA8LSBkYXRhLmZyYW1lKCB0b3RhbFJlYWRzID0gY29sU3VtcyhkYXRhXzEwWF9OU0NzKSAsIGdlbmVSZWFkcyA9IGFzLm51bWVyaWMoZGF0YV8xMFhfTlNDc1siRU5TTVVTRzAwMDAwMDczNTU1IixjZWxsc19OU0NzXzEwWF0pICwgZ2VuZSA9ICJHbTQ5NTEiICAgKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJuYW1lIikgJT4lIGxlZnRfam9pbihhbm5vXzEwWF9OU0NzICwgYnkgPSAibmFtZSIgKQpgYGAKCmBgYHtyfQphZGRtYXJnaW5zKCB0YWJsZSggR200OTUxX2RmJGFnZSAgLCBHbTQ5NTFfZGYkZ2VuZVJlYWRzID4gMCApICkKYGBgCgpgYGB7ciBmaWcud2lkdGg9NiAsIGZpZy5oZWlnaHQ9Mn0KZ2dfR200OTUxIDwtIGdncGxvdChkYXRhID0gR200OTUxX2RmICwgbWFwcGluZyA9IGFlcyh4ID0gdG90YWxSZWFkcyAsIHkgPSBnZW5lUmVhZHMgKSApICsgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBhZ2UpKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJvbGQiID0gInNsYXRlYmx1ZSIgLCAieW91bmciID0gInllbGxvd2dyZWVuIikgKQoKZ2dfR200OTUxCmBgYAoKIyMjIFNsYzFhMyAtIEVOU01VU0cwMDAwMDAwNTM2MAoKYGBge3J9ClNsYzFhM19kZiA8LSBkYXRhLmZyYW1lKCB0b3RhbFJlYWRzID0gY29sU3VtcyhkYXRhXzEwWF9OU0NzKSAsIGdlbmVSZWFkcyA9IGFzLm51bWVyaWMoZGF0YV8xMFhfTlNDc1siRU5TTVVTRzAwMDAwMDA1MzYwIixjZWxsc19OU0NzXzEwWF0pICwgZ2VuZSA9ICJTbGMxYTMiICAgKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJuYW1lIikgJT4lIGxlZnRfam9pbihhbm5vXzEwWF9OU0NzICwgYnkgPSAibmFtZSIgKQpgYGAKCmBgYHtyfQphZGRtYXJnaW5zKCB0YWJsZSggU2xjMWEzX2RmJGFnZSAgLCBTbGMxYTNfZGYkZ2VuZVJlYWRzID4gMCApICkKYGBgCgpgYGB7ciBmaWcud2lkdGg9NiAsIGZpZy5oZWlnaHQ9Mn0KZ2dfU2xjMWEzIDwtIGdncGxvdChkYXRhID0gU2xjMWEzX2RmICwgbWFwcGluZyA9IGFlcyh4ID0gdG90YWxSZWFkcyAsIHkgPSBnZW5lUmVhZHMgKSApICsgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBhZ2UpKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJvbGQiID0gInNsYXRlYmx1ZSIgLCAieW91bmciID0gInllbGxvd2dyZWVuIikgKQoKZ2dfU2xjMWEzCmBgYAoKIyMjIFNveDIgLSBFTlNNVVNHMDAwMDAwNzQ2MzcKCmBgYHtyfQpTb3gyX2RmIDwtIGRhdGEuZnJhbWUoIHRvdGFsUmVhZHMgPSBjb2xTdW1zKGRhdGFfMTBYX05TQ3MpICwgZ2VuZVJlYWRzID0gYXMubnVtZXJpYyhkYXRhXzEwWF9OU0NzWyJFTlNNVVNHMDAwMDAwNzQ2MzciLGNlbGxzX05TQ3NfMTBYXSkgLCBnZW5lID0gIlNveDIiICAgKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJuYW1lIikgJT4lIGxlZnRfam9pbihhbm5vXzEwWF9OU0NzICwgYnkgPSAibmFtZSIgKQpgYGAKCmBgYHtyfQphZGRtYXJnaW5zKCB0YWJsZSggU294Ml9kZiRhZ2UgICwgU294Ml9kZiRnZW5lUmVhZHMgPiAwICkgKQpgYGAKCgpgYGB7ciBmaWcud2lkdGg9NiAsIGZpZy5oZWlnaHQ9Mn0KZ2dfU294MiA8LSBnZ3Bsb3QoZGF0YSA9IFNveDJfZGYgLCBtYXBwaW5nID0gYWVzKHggPSB0b3RhbFJlYWRzICwgeSA9IGdlbmVSZWFkcyApICkgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGFnZSkpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIm9sZCIgPSAic2xhdGVibHVlIiAsICJ5b3VuZyIgPSAieWVsbG93Z3JlZW4iKSApCgpnZ19Tb3gyCmBgYAoKYGBge3J9CmludGVyZmVyb25fZ2VuZXNfYnVsa19kZiA8LSBiaW5kX3Jvd3MoIElmaXQxX2RmICwgQzNfZGYgLCBSc2FkMl9kZiAsIElpZ3AxX2RmICwgSWZpNDdfZGYgLCBJbDMzX2RmICwgQ3hjbDEwX2RmICwgU294Ml9kZiAsIFNsYzFhM19kZiApCmBgYAoKIyMjIENvbWJpbmUgYWxsIGdlbmVzCgojIyMjIFNjYXR0ZXJwbG90CgpgYGB7cn0KZ2dfaW50ZXJmZXJvbl9nZW5lcyA8LSBnZ3Bsb3QoZGF0YSA9IGludGVyZmVyb25fZ2VuZXNfYnVsa19kZiAsIG1hcHBpbmcgPSBhZXMoeCA9IHRvdGFsUmVhZHMgLCB5ID0gZ2VuZVJlYWRzICkgKSArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gYWdlKSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygib2xkIiA9ICJzbGF0ZWJsdWUiICwgInlvdW5nIiA9ICJ5ZWxsb3dncmVlbiIpICkgKyBmYWNldF9ncmlkKCBnZW5lIH4gLiApCmBgYAoKCmBgYHtyIGZpZy53aWR0aD0xMCAsIGZpZy5oZWlnaHQ9MTJ9CmdnX2ludGVyZmVyb25fZ2VuZXMKYGBgCgojIyMjIEJhcnBsb3QKClNob3cgdGhlIHNhbWUgZGF0YSBhcyBzdGFja2VkIGJhcnBsb3RzCgpgYGB7cn0KaW50ZXJmZXJvbl9nZW5lc19idWxrX2RmX2JhcnMgPC0gaW50ZXJmZXJvbl9nZW5lc19idWxrX2RmICU+JSBtdXRhdGUoIGludGVydmFsID0gYmFzZTo6Y3V0KCBnZW5lUmVhZHMgLCBicmVha3MgPSBjKC0wLjEsIDAuNSw1LjUsNTAuNSxJbmYpICwgbGFiZWxzID0gYygiMCIsIjEtNSIsIjUtNTAiLCI1MC0iKSApICkgJT4lIGdyb3VwX2J5KGludGVydmFsLGdlbmUsYWdlKSAlPiUgc3VtbWFyaXNlKCBjb3VudHMgPSBuKCkgKSAlPiUgdW5ncm91cCgpICU+JSBtdXRhdGUoIGludGVydmFsID0gZmN0X3JldiggaW50ZXJ2YWwgKSAsIGFnZSA9IGZjdF9yZWNvZGUoIC5mID0gYWdlICwgICIyMiBtb250aCIgPSAib2xkIiwgICIyIG1vbnRoIiA9ICJ5b3VuZyIgKSAlPiUgZmN0X3JldigpICkgJT4lIGdyb3VwX2J5KGdlbmUsYWdlKSAlPiUgbXV0YXRlKCBQZXJjZW50ID0gMTAwKmNvdW50cy9zdW0oY291bnRzKSApCmBgYAoKYGBge3IgZmlnLndpZHRoPTE4LCBmaWcuaGVpZ2h0PTV9CmdncGxvdCggZGF0YSA9IGludGVyZmVyb25fZ2VuZXNfYnVsa19kZl9iYXJzICwgbWFwcGluZyA9IGFlcyh4ID0gYWdlICwgeSA9IFBlcmNlbnQgLCBmaWxsID0gaW50ZXJ2YWwpICkgKyBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKyBmYWNldF9ncmlkKGZhY2V0cyA9IC4gfiBnZW5lICkgKyB2aXJpZGlzOjpzY2FsZV9maWxsX3ZpcmlkaXMoIGRpc2NyZXRlID0gVFJVRSAsIGRpcmVjdGlvbiA9IC0xICkgKyBnZ3RpdGxlKCJSZWFkIGNvdW50czogMTBYIikKYGBgCgoKIyMjIEJ1bGsgTlNDIGdlbmVzIGluIElubmF0ZURCCgpXaGljaCBnZW5lcyBhcmUgdGhlIG9uZXMgd2l0aCBoaWdoIGZvbGQgY2hhbmdlcyBpbiB0aGUgYnVsayBhbmQgYXJlIGFubm90YXRlZCBpbiBJbm5hdGVEQgoKYGBge3J9CmlubmF0ZURCX2dlbmVzX05TQ3NfYnVsa191cF90YWJsZSA8LSBzdHJ1Y3R1cmUobGlzdChlbnNlbWJsX2dlbmVfaWQgPSBzdHJ1Y3R1cmUoYygyNEwsIDI1TCwgOUwsIDQwTCwgCjM0TCwgMTVMLCAxN0wsIDMxTCwgMjJMLCAzMEwsIDI2TCwgMjBMLCAxOUwsIDMzTCwgMjdMLCA0TCwgMTZMLCAKOEwsIDdMLCAyOUwsIDM1TCwgMUwsIDEyTCwgMkwsIDMyTCwgMTNMLCAzNkwsIDEwTCwgM0wsIDI4TCwgMjFMLCAKMzlMLCAyM0wsIDExTCwgMTRMLCA2TCwgNUwsIDE4TCwgMzhMLCAzN0wpLCAuTGFiZWwgPSBjKCJFTlNNVVNHMDAwMDAwMDAzODYiLCAKIkVOU01VU0cwMDAwMDAwMjMyNSIsICJFTlNNVVNHMDAwMDAwMDU0MTMiLCAiRU5TTVVTRzAwMDAwMDE0MzYxIiwgCiJFTlNNVVNHMDAwMDAwMTU3NjYiLCAiRU5TTVVTRzAwMDAwMDE3NzE2IiwgIkVOU01VU0cwMDAwMDAxODg5OSIsIAoiRU5TTVVTRzAwMDAwMDIwMTIyIiwgIkVOU01VU0cwMDAwMDAyMDY0MSIsICJFTlNNVVNHMDAwMDAwMjE1MDgiLCAKIkVOU01VU0cwMDAwMDAyMjMwNiIsICJFTlNNVVNHMDAwMDAwMjM5NTEiLCAiRU5TTVVTRzAwMDAwMDI0MDY2IiwgCiJFTlNNVVNHMDAwMDAwMjQwNzkiLCAiRU5TTVVTRzAwMDAwMDI0MTY0IiwgIkVOU01VU0cwMDAwMDAyNTQ5MiIsIAoiRU5TTVVTRzAwMDAwMDI1NDk4IiwgIkVOU01VU0cwMDAwMDAyNjEwNCIsICJFTlNNVVNHMDAwMDAwMjY4OTYiLCAKIkVOU01VU0cwMDAwMDAyODI3MCIsICJFTlNNVVNHMDAwMDAwMjkxNjciLCAiRU5TTVVTRzAwMDAwMDI5ODI2IiwgCiJFTlNNVVNHMDAwMDAwMzAzNDEiLCAiRU5TTVVTRzAwMDAwMDM0NDU5IiwgIkVOU01VU0cwMDAwMDAzNDg1NSIsIAoiRU5TTVVTRzAwMDAwMDM1NjkyIiwgIkVOU01VU0cwMDAwMDAzNjkwNSIsICJFTlNNVVNHMDAwMDAwMzY5MTMiLCAKIkVOU01VU0cwMDAwMDAzNzg2MCIsICJFTlNNVVNHMDAwMDAwMzg0MTgiLCAiRU5TTVVTRzAwMDAwMDM4NjQyIiwgCiJFTlNNVVNHMDAwMDAwNDA3NDciLCAiRU5TTVVTRzAwMDAwMDQ2Njg4IiwgIkVOU01VU0cwMDAwMDA0NjcxOCIsIAoiRU5TTVVTRzAwMDAwMDUzMTEzIiwgIkVOU01VU0cwMDAwMDA1NDcxNyIsICJFTlNNVVNHMDAwMDAwNjA0NDEiLCAKIkVOU01VU0cwMDAwMDA2NDIxNSIsICJFTlNNVVNHMDAwMDAwNzQxNTEiLCAiRU5TTVVTRzAwMDAwMDc0ODk2IgopLCBjbGFzcyA9ICJmYWN0b3IiKSwgYmFzZU1lYW4gPSBjKDE0MjQuOTU5NzYyMDU3MzUsIDE0Ni42OTMzODQ5ODA4ODYsIAoyNTQuNzQ3MjU3NjI0OTkzLCAxMzc4Ljc0MjQwODM2Mjc3LCAyMzIuNjc5MjIyODY0Njg5LCA1My4zODU2MTQ3MDAzMzc2LCAKMTI3LjI5NTI0Mzg2NDQ2NywgMTAwLjg1ODgyNzg2NTQ1MiwgMTI1NC40ODIyNjg2OTgyMSwgODQ4NC41NTgwODM2NjQzMywgCjEzMy4zOTE1ODQ3Nzg0ODcsIDQ0OS42OTg1NjMxMDkyNjgsIDQzNS45NzAzMzg3OTQ5ODEsIDQ4Ny42NTI2MTE3NDg3NTIsIAozOC4xMzI0ODcxODQ4NjY5LCAyMjcwLjQ0NTg1MTU4OTI4LCAzOTIuNDU1NzkzNDE2NjE0LCA4MTIzLjQ0NTk0NDYzNjEsIAo0OTUuNTEzOTIzMzI0MTU1LCAxNDcuNjM0NzQyMzczNzE5LCAyODUuNDM3ODM3MTIwMzIyLCAyMC4wNDg1MTA0NzA0ODMzLCAKMjQzLjY5MTk5Njg5NzIxNiwgNTEzLjkyNDk5NTEwNzIwMiwgMTcuNDE5MDYyMjM1Njc1OSwgMTguMTQzNjU5NTIwNjMzMSwgCjE1ODkwLjAwOTY2OTUxMzUsIDE5NTIuNjY1NzM1MzcwNTMsIDE0OS4xMjc1NDUxNzAxMDgsIDI4NC4wNDYzODA3NjE3MjMsIAoxMjU3LjMwNTE4MTI3MzU3LCA1Mi44ODUxOTMwNzgxMjA5LCA3NjEuOTY2NjAzMjMwODcxLCAzMTUuMjgzOTU4Mjk3Mzk0LCAKNjMzLjkzMjI1NDc3NjU0NiwgMTA5MS44NzU3MjUzNzkzNywgNDUwLjYxNDYyNjY4NTQyLCAxNzU3LjI1MDIyOTE3NDU2LCAKMjQyNS41NjY1NDI1NjM3MywgNjQuNjA5MDk1MjgxNTg5MyksIGxvZzJGb2xkQ2hhbmdlID0gYyg0LjU1MjQyODU5OTM4Mzc0LCAKNi45NTY3ODQxMTEzMTM2MiwgNS4wMzY0MTc3NTEzNDMyOSwgMy4zNzk2NjI1NzUzMjE1OSwgMy43MzQyODYxMDc3ODI5NiwgCjUuNTMxNDE4Mjk2MDYwODEsIDMuODYxMjE5NjI0NDQ3OTcsIDMuNzU1MzE0NTkxMjcyMzQsIDIuNDIzNTYwNTI2MDk1ODQsIAoyLjE2Mzc0MjM4NjcwOTI4LCAzLjE5NzE1MDYzNTEyNzYsIDIuNDQwNjMxMTQwMTQ3NzksIDIuNDQ2Nzc4NTg2NzA4MDcsIAoyLjA4OTM3NjIyNjExMTQsIDMuNjcxOTA1ODY4NjMxMjksIDEuODg3NTU0ODEwNTUzNjQsIDIuMTAwMzg4MDAxNzExNjgsIAoxLjY5ODQ1MDcyNTcyOTI4LCAxLjkxMTgzNzQ0ODE5NDU3LCAyLjQxNDAzNDIwMDg4NjE5LCAyLjAyODQ5Mjk0OTY5ODIsIAozLjcwMTQ1ODQ0OTUxMTksIDEuOTQxMDUyNDA5NjczNjcsIDEuOTgzMzMxOTIzMzgzMzgsIDMuNDQ0ODM0NDk2OTA4MSwgCjMuNDQzNjM2ODIyMzkzMjksIDEuNTcyODEwMzc2ODQ5ODksIDEuNjM4ODM0ODI3MzQ1MDgsIDIuMTE3MTczNjkxMTEzMDYsIAoxLjg2NjgxNDYyNTY4OTMsIDEuNDMzODYxODcxOTMzODksIDIuNzYwMzg5Njg2Njg2ODYsIDEuNzU4MDYxNjQzOTk1MSwgCjEuNTI4MTA4OTYwMjgyMTgsIDEuNTUzOTQ0NTE2MzMzNzksIDEuMjgwNTY3OTk1Njc2ODEsIDEuNDIyNDI1MTMzNzUyOTYsIAoxLjI2NzM1NTM2NTA4MDgzLCAxLjE4NTcwODA4NzY2NjY4LCAyLjEyODEwMzE5OTU4Mzk3KSwgbGZjU0UgPSBjKDAuNTE5Mzg0MjQzMjAzMjMyLCAKMC44MTM4ODQ4ODI1NTA5MywgMC42NDE5NTE0NDQyNjM3MzQsIDAuNDg2MTIxNzU3ODYyNjksIDAuNTc4NTE0OTM1NTA4NzM1LCAKMC44Nzg1ODk1NjgyNDA2ODQsIDAuNjcwNTc4NjczODk0OTQxLCAwLjY4NDUzNTk1ODI4NDc1MSwgMC40NDg4MDQyOTE5NjU4MjcsIAowLjQxMzc5MDQ2MTAxMzg1LCAwLjY0NTgxNjc0MTc3MDAwNSwgMC41MDQ1NzUxMTI1NzUwNzgsIDAuNTMxOTY2MDQzNjM1OTA0LCAKMC40NjA5NDYzODIyMDg4MDIsIDAuODQ1NTcyOTA5NzA2MTkxLCAwLjQzNjE4MzExMTE1Nzg3NywgMC41MDUwMTE0MDIwNTgyNzgsIAowLjQxNTgwNzcwNTYxMTc1OSwgMC40ODgxODMzODA3MjI1ODksIDAuNjE4ODA4NDAzMjI0MDA1LCAwLjUyMDY3NjIwNTM3MjY0NiwgCjAuOTY1NzM4MjE1OTQ3MzY3LCAwLjUyMTE1MzMwNzAxMDc5OCwgMC41NTMzNjc4MzQ4NzQ0MDMsIDAuOTczODkxMTkxMzY3MzAxLCAKMC45NzQ0ODMyMjI2Mzk3MiwgMC40NDg4OTUyNDA2OTIwNjUsIDAuNDY4ODA2NzQzNjA5MTIzLCAwLjYwNjY1Mjg3NTc3Njc1OCwgCjAuNTQxNDU1MjcyMDQwODQzLCAwLjQyMTE4NzEyODA5NjQ3LCAwLjgxODQ5NzMwNDg5NDg1NiwgMC41MzU0MjY1ODAyNjAxMjEsIAowLjQ4ODE1OTY1NzgwNDQzOSwgMC41MDA4NjgyOTUxNDI2MjMsIDAuNDIyMjc2NzcwMTgxOTkzLCAwLjQ3MDYzMDgzMzQ4NTkwNiwgCjAuNDI3NjIyMzQ3NTI5NDcyLCAwLjQwNjE0MjE5NTcwMDMxOCwgMC43Mjk4NDMzMTQ2NDUyKSwgc3RhdCA9IGMoOC43NjUwNDk0OTY1MTAwNCwgCjguNTQ3NjI2NjQ5MDAzNzUsIDcuODQ1NDgwODMyNDYzMzYsIDYuOTUyMjk2NDU3OTUwNzksIDYuNDU0OTUxOTQ0MzI0NjUsIAo2LjI5NTc5MzI3NTk2Mjg1LCA1Ljc1ODA0MTE4ODU0NjUxLCA1LjQ4NTkyNzQzMTMwOTkyLCA1LjQwMDAzODY1NzExNzc4LCAKNS4yMjkwNzc0OTM0OTIyLCA0Ljk1MDU1Mzk3MDQxMTMyLCA0LjgzNzAwMjYxNzI5NTQzLCA0LjU5OTUwMTQ0NTU4OTg5LCAKNC41MzI3OTY2Njk1Mzc0NywgNC4zNDI1MDY1MTQyMDEzMSwgNC4zMjc0MzY3MTY5ODU2NywgNC4xNTkwOTAyNTY0Nzk2LCAKNC4wODQ3MDIzODIzOTE5NiwgMy45MTYyMjgwNDgwODQ2MiwgMy45MDExMDExOTQzNDIyNSwgMy44OTU4ODE3OTUxODY3MywgCjMuODMyNzc2MTk5OTk4MzIsIDMuNzI0NTMyNDYyMDYzOSwgMy41ODQxMTEzMjQxMzEzMywgMy41MzcxODYyMTQ4OTA5MSwgCjMuNTMzODA4MjE5OTc2MzUsIDMuNTAzNzM1OTE0OTIwOTksIDMuNDk1NzU3NzkyOTIwMDgsIDMuNDg5OTI2MDc3NDE2NTcsIAozLjQ0Nzc3MjU1MzEyODc5LCAzLjQwNDMzNDUwMTg5NzM4LCAzLjM3MjUwOTE5NDgxMjE0LCAzLjI4MzQ3ODQ2MTQ5MzI2LCAKMy4xMzAzNDY2NzIxNDIxNywgMy4xMDI1MDEyNTkxMjEwMiwgMy4wMzI1MzI0MTk3MzI0NywgMy4wMjIzNzk4MTk5MDUxOCwgCjIuOTYzNzI1NzU1Njg3OTIsIDIuOTE5NDQwNzc3Njg2NjEsIDIuOTE1ODM1NzEwMDUwMzMpLCBwdmFsdWUgPSBjKDEuODY2OTQ2MTg4MjA3MDhlLTE4LCAKMS4yNTY0NTM4NTUyMjA4OWUtMTcsIDQuMzEyOTc3MjAwMzc1NzRlLTE1LCAzLjU5Mzg3MTU3NjEyMjA5ZS0xMiwgCjEuMDgyNTMyNDg0Nzk0NzNlLTEwLCAzLjA1ODMxNzMyNjc4MDM1ZS0xMCwgOC41MDk1NTk0NjA3MTI2OGUtMDksIAo0LjExMzA1NzMyNzgwMzE3ZS0wOCwgNi42NjI2NTM4NDE4NDU4OGUtMDgsIDEuNzAzNTc5NTE2ODA4NzRlLTA3LCAKNy40MDAyNTMwODY3OTgxNmUtMDcsIDEuMzE4MTE2OTMxNjcyNjhlLTA2LCA0LjIzNTAzMjU0MTEzNzQzZS0wNiwgCjUuODIwNzg0MDExNTM5OTJlLTA2LCAxLjQwODY2MzA3Mzk5NTUyZS0wNSwgMS41MDg1NDY2Nzk0NjU2OGUtMDUsIAozLjE5NTE3NjM1Mzc2ODcxZS0wNSwgNC40MTMzMzQ2NDMxODUzMmUtMDUsIDguOTk0NTE3MTQ2OTQxNDllLTA1LCAKOS41NzU2MTA5NzA5NTM0N2UtMDUsIDkuNzg0MjEwMTM4NTQyNThlLTA1LCAwLjAwMDEyNjcwNTIxNDc0NTM2NiwgCjAuMDAwMTk1Njc3NTMyOTI1NDUxLCAwLjAwMDMzODIyNzgxNDI5MTg5NiwgMC4wMDA0MDQ0MTQ0MjAxNzkwMDUsIAowLjAwMDQwOTYxODE4MDA2NjQ1MywgMC4wMDA0NTg3ODAwODE1ODM4LCAwLjAwMDQ3MjcxNzU3Nzg2MzE5OCwgCjAuMDAwNDgzMTU0MTc1MzMzMjg5LCAwLjAwMDU2NTIyOTgwMDc1ODAyOSwgMC4wMDA2NjMyNTQ3NzkzMjA1MjEsIAowLjAwMDc0NDg2NTk5MzY2NTUzOCwgMC4wMDEwMjUzNDQ3OTQ5Njg4MywgMC4wMDE3NDYwMDExOTc3NjkxOSwgCjAuMDAxOTE4OTI3MzE2NDA2NjEsIDAuMDAyNDI1MTEwNDYwNjUyMjUsIDAuMDAyNTA3OTU2NDY2MjAxOTcsIAowLjAwMzAzOTM5MDI2MjI5Njg2LCAwLjAwMzUwNjYwMDE1NDg0NDExLCAwLjAwMzU0NzM3MjQxOTY0MjA5CiksIHBhZGogPSBjKDIuNzY4NDk0NTAyNDkyMjdlLTE1LCAxLjQzMzIyNzI0NzYyMDgxZS0xNCwgMy41NTMxNzQzODM1NzYyMWUtMTIsIAoxLjE1ODU1NDgxNzQ0MTYyZS0wOSwgMi40MzIyNTM2NjkyNDU2MWUtMDgsIDUuOTY3MzQwNDc4NzkyODhlLTA4LCAKMS4xMDY5MTQ1MzcyMTg0OWUtMDYsIDQuMDkzNDU4MTk1NTcwMDJlLTA2LCA2LjAyNDQyMDM1NDkyMjcyZS0wNiwgCjEuNDAzNDY1NTkxOTMwOTRlLTA1LCA1LjA1NzA2Njk1OTYzNzMzZS0wNSwgNy43MjU4MzI0MDMwNzI3OWUtMDUsIAowLjAwMDIwODY0MjE4NDU1OTg5LCAwLjAwMDI3MjI5MTUwMTkxNTIyMiwgMC4wMDA1NTUxNDMzMTAzNDQxMzksIAowLjAwMDU4NTYwODM0MzE4ODM5LCAwLjAwMTA5MTczNDMzNTI1NDI5LCAwLjAwMTQzNTIwNDgxMTkyNTMzLCAKMC4wMDI2MTAxNzAxNTIwOTM4NCwgMC4wMDI3NDEyNDk3MTIxMjg3NCwgMC4wMDI3ODQ4Mzc4NTMwNjA0MiwgCjAuMDAzNDEzMjY5NTg4MTI3MzcsIDAuMDA0ODYwNDcyNTg5MTk4NTIsIDAuMDA3NTQyMjI1OTUyMDgyLCAKMC4wMDg2OTEzOTMzODY3MTY2MSwgMC4wMDg3Mzk4OTYzOTE2NjI0OSwgMC4wMDk1OTU1NTY4ODI2NjAzMiwgCjAuMDA5ODE3ODI3Njc4MDU3OTIsIDAuMDA5OTM3MTYxMjU2NjEyMTIsIDAuMDExMzI2NzQ2OTEyNzU3OSwgCjAuMDEyODMxODkyNjk2MDkzOCwgMC4wMTQwNTI5NDg4ODA0OTE0LCAwLjAxODAwMjgyODQxMTM4NTIsIDAuMDI3MzQ3NzI2NjgzOTc1MiwgCjAuMDI5NDg3ODQ3ODQ5NzM0MywgMC4wMzQ5ODk5Mzc3Nzk2ODQsIDAuMDM1NzI1NzMxNDQ3OTQzMywgMC4wNDE1NzkzOTExMTIwMTU2LCAKMC4wNDU3NzQwOTY1NjM1NDE3LCAwLjA0NjIyNDk0MzQxOTA0NDQpLCBnZW5lX3N5bWJvbCA9IGMoIklmaXQxIiwgCiJDeGNsMTAiLCAiUnNhZDIiLCAiSWZpdDMiLCAiQnN0MiIsICJDMyIsICJJcmY3IiwgIkN0c3MiLCAiWmMzaGF2MSIsIAoiRWdyMSIsICJJc2cxNSIsICJHYnAyIiwgIklmaWgxIiwgIlRpZmEiLCAiQzFxYiIsICJNZXJ0ayIsICJJZml0bTMiLCAKIkVnZnIiLCAiSXJmMSIsICJBaW0yIiwgIlNvY3MzIiwgIk14MSIsICJWZWdmYSIsICJJcmY5IiwgIkNkNTMiLCAKIlhkaCIsICJIbWdiMiIsICJDeGNsMTQiLCAiSG1veDEiLCAiVHJpbTY3IiwgIlBwYXJnYzFhIiwgIk5scmM1IiwgCiJUbmZyc2YxYSIsICJaZnBtMiIsICJFaWYyYWsyIiwgIkJpcmM1IiwgIkVwczgiLCAiU3RhdDEiLCAiSWZpMjciLCAKIlRyaW01IiksIGVudHJlemdlbmUgPSBjKDE1OTU3TCwgMTU5NDVMLCA1ODE4NUwsIDE1OTU5TCwgNjk1NTBMLCAKMTAyNjQyMjUxTCwgNTQxMjNMLCAxMzA0MEwsIDc4NzgxTCwgMTM2NTNMLCAxMDAwMzg4ODJMLCAxNDQ2OUwsIAo3MTU4NkwsIDIxMTU1MEwsIDEyMjYwTCwgMTcyODlMLCA2NjE0MUwsIDEzNjQ5TCwgMTYzNjJMLCAzODM2MTlMLCAKMTI3MDJMLCBOQSwgMjIzMzlMLCAxNjM5MUwsIDEyNTA4TCwgMjI0MzZMLCA5NzE2NUwsIDU3MjY2TCwgMTUzNjhMLCAKMzMwODYzTCwgMTkwMTdMLCA0MzQzNDFMLCAyMTkzN0wsIDIyNzYyTCwgMTkxMDZMLCAxMTc5OUwsIDEzODYwTCwgCjIwODQ2TCwgNTI2NjhMLCA2Njc4MjNMKSwgZGVzY3JpcHRpb24gPSBzdHJ1Y3R1cmUoYygxOUwsIDZMLCAKMzBMLCAyMEwsIDNMLCAxMEwsIDI0TCwgNEwsIDM5TCwgMTFMLCAyNkwsIDE1TCwgMjJMLCAzM0wsIDlMLCAKOEwsIDIxTCwgMTNMLCAyM0wsIDFMLCAzMkwsIDI3TCwgMzdMLCAyNUwsIDVMLCAzOEwsIDE3TCwgN0wsIAoxNkwsIDM1TCwgMjlMLCAyOEwsIDM2TCwgNDBMLCAxNEwsIDJMLCAxMkwsIDMxTCwgMThMLCAzNEwpLCAuTGFiZWwgPSBjKCJhYnNlbnQgaW4gbWVsYW5vbWEgMiBbU291cmNlOk1HSSBTeW1ib2w7QWNjOk1HSToyNjg2MTU5XSIsIAoiYmFjdWxvdmlyYWwgSUFQIHJlcGVhdC1jb250YWluaW5nIDUgW1NvdXJjZTpNR0kgU3ltYm9sO0FjYzpNR0k6MTIwMzUxN10iLCAKImJvbmUgbWFycm93IHN0cm9tYWwgY2VsbCBhbnRpZ2VuIDIgW1NvdXJjZTpNR0kgU3ltYm9sO0FjYzpNR0k6MTkxNjgwMF0iLCAKImNhdGhlcHNpbiBTIFtTb3VyY2U6TUdJIFN5bWJvbDtBY2M6TUdJOjEwNzM0MV0iLCAiQ0Q1MyBhbnRpZ2VuIFtTb3VyY2U6TUdJIFN5bWJvbDtBY2M6TUdJOjg4MzQxXSIsIAoiY2hlbW9raW5lIChDLVgtQyBtb3RpZikgbGlnYW5kIDEwIFtTb3VyY2U6TUdJIFN5bWJvbDtBY2M6TUdJOjEzNTI0NTBdIiwgCiJjaGVtb2tpbmUgKEMtWC1DIG1vdGlmKSBsaWdhbmQgMTQgW1NvdXJjZTpNR0kgU3ltYm9sO0FjYzpNR0k6MTg4ODUxNF0iLCAKImMtbWVyIHByb3RvLW9uY29nZW5lIHR5cm9zaW5lIGtpbmFzZSBbU291cmNlOk1HSSBTeW1ib2w7QWNjOk1HSTo5Njk2NV0iLCAKImNvbXBsZW1lbnQgY29tcG9uZW50IDEsIHEgc3ViY29tcG9uZW50LCBiZXRhIHBvbHlwZXB0aWRlIFtTb3VyY2U6TUdJIFN5bWJvbDtBY2M6TUdJOjg4MjI0XSIsIAoiY29tcGxlbWVudCBjb21wb25lbnQgMyBbU291cmNlOk1HSSBTeW1ib2w7QWNjOk1HSTo4ODIyN10iLCAiZWFybHkgZ3Jvd3RoIHJlc3BvbnNlIDEgW1NvdXJjZTpNR0kgU3ltYm9sO0FjYzpNR0k6OTUyOTVdIiwgCiJlcGlkZXJtYWwgZ3Jvd3RoIGZhY3RvciByZWNlcHRvciBwYXRod2F5IHN1YnN0cmF0ZSA4IFtTb3VyY2U6TUdJIFN5bWJvbDtBY2M6TUdJOjEwNDY4NF0iLCAKImVwaWRlcm1hbCBncm93dGggZmFjdG9yIHJlY2VwdG9yIFtTb3VyY2U6TUdJIFN5bWJvbDtBY2M6TUdJOjk1Mjk0XSIsIAoiZXVrYXJ5b3RpYyB0cmFuc2xhdGlvbiBpbml0aWF0aW9uIGZhY3RvciAyLWFscGhhIGtpbmFzZSAyIFtTb3VyY2U6TUdJIFN5bWJvbDtBY2M6TUdJOjEzNTM0NDldIiwgCiJndWFueWxhdGUgYmluZGluZyBwcm90ZWluIDIgW1NvdXJjZTpNR0kgU3ltYm9sO0FjYzpNR0k6MTAyNzcyXSIsIAoiaGVtZSBveHlnZW5hc2UgKGRlY3ljbGluZykgMSBbU291cmNlOk1HSSBTeW1ib2w7QWNjOk1HSTo5NjE2M10iLCAKImhpZ2ggbW9iaWxpdHkgZ3JvdXAgYm94IDIgW1NvdXJjZTpNR0kgU3ltYm9sO0FjYzpNR0k6OTYxNTddIiwgCiJpbnRlcmZlcm9uLCBhbHBoYS1pbmR1Y2libGUgcHJvdGVpbiAyNyBbU291cmNlOk1HSSBTeW1ib2w7QWNjOk1HSToxMjc3MTgwXSIsIAoiaW50ZXJmZXJvbi1pbmR1Y2VkIHByb3RlaW4gd2l0aCB0ZXRyYXRyaWNvcGVwdGlkZSByZXBlYXRzIDEgW1NvdXJjZTpNR0kgU3ltYm9sO0FjYzpNR0k6OTk0NTBdIiwgCiJpbnRlcmZlcm9uLWluZHVjZWQgcHJvdGVpbiB3aXRoIHRldHJhdHJpY29wZXB0aWRlIHJlcGVhdHMgMyBbU291cmNlOk1HSSBTeW1ib2w7QWNjOk1HSToxMTAxMDU1XSIsIAoiaW50ZXJmZXJvbiBpbmR1Y2VkIHRyYW5zbWVtYnJhbmUgcHJvdGVpbiAzIFtTb3VyY2U6TUdJIFN5bWJvbDtBY2M6TUdJOjE5MTMzOTFdIiwgCiJpbnRlcmZlcm9uIGluZHVjZWQgd2l0aCBoZWxpY2FzZSBDIGRvbWFpbiAxIFtTb3VyY2U6TUdJIFN5bWJvbDtBY2M6TUdJOjE5MTg4MzZdIiwgCiJpbnRlcmZlcm9uIHJlZ3VsYXRvcnkgZmFjdG9yIDEgW1NvdXJjZTpNR0kgU3ltYm9sO0FjYzpNR0k6OTY1OTBdIiwgCiJpbnRlcmZlcm9uIHJlZ3VsYXRvcnkgZmFjdG9yIDcgW1NvdXJjZTpNR0kgU3ltYm9sO0FjYzpNR0k6MTg1OTIxMl0iLCAKImludGVyZmVyb24gcmVndWxhdG9yeSBmYWN0b3IgOSBbU291cmNlOk1HSSBTeW1ib2w7QWNjOk1HSToxMDc1ODddIiwgCiJJU0cxNSB1YmlxdWl0aW4tbGlrZSBtb2RpZmllciBbU291cmNlOk1HSSBTeW1ib2w7QWNjOk1HSToxODU1Njk0XSIsIAoibXl4b3ZpcnVzIChpbmZsdWVuemEgdmlydXMpIHJlc2lzdGFuY2UgMSBbU291cmNlOk1HSSBTeW1ib2w7QWNjOk1HSTo5NzI0M10iLCAKIk5MUiBmYW1pbHksIENBUkQgZG9tYWluIGNvbnRhaW5pbmcgNSBbU291cmNlOk1HSSBTeW1ib2w7QWNjOk1HSTozNjEyMTkxXSIsIAoicGVyb3hpc29tZSBwcm9saWZlcmF0aXZlIGFjdGl2YXRlZCByZWNlcHRvciwgZ2FtbWEsIGNvYWN0aXZhdG9yIDEgYWxwaGEgW1NvdXJjZTpNR0kgU3ltYm9sO0FjYzpNR0k6MTM0Mjc3NF0iLCAKInJhZGljYWwgUy1hZGVub3N5bCBtZXRoaW9uaW5lIGRvbWFpbiBjb250YWluaW5nIDIgW1NvdXJjZTpNR0kgU3ltYm9sO0FjYzpNR0k6MTkyOTYyOF0iLCAKInNpZ25hbCB0cmFuc2R1Y2VyIGFuZCBhY3RpdmF0b3Igb2YgdHJhbnNjcmlwdGlvbiAxIFtTb3VyY2U6TUdJIFN5bWJvbDtBY2M6TUdJOjEwMzA2M10iLCAKInN1cHByZXNzb3Igb2YgY3l0b2tpbmUgc2lnbmFsaW5nIDMgW1NvdXJjZTpNR0kgU3ltYm9sO0FjYzpNR0k6MTIwMTc5MV0iLCAKIlRSQUYtaW50ZXJhY3RpbmcgcHJvdGVpbiB3aXRoIGZvcmtoZWFkLWFzc29jaWF0ZWQgZG9tYWluIFtTb3VyY2U6TUdJIFN5bWJvbDtBY2M6TUdJOjIxODI5NjVdIiwgCiJ0cmlwYXJ0aXRlIG1vdGlmLWNvbnRhaW5pbmcgNSBbU291cmNlOk1HSSBTeW1ib2w7QWNjOk1HSTozNjQ2ODUzXSIsIAoidHJpcGFydGl0ZSBtb3RpZi1jb250YWluaW5nIDY3IFtTb3VyY2U6TUdJIFN5bWJvbDtBY2M6TUdJOjMwNDUzMjNdIiwgCiJ0dW1vciBuZWNyb3NpcyBmYWN0b3IgcmVjZXB0b3Igc3VwZXJmYW1pbHksIG1lbWJlciAxYSBbU291cmNlOk1HSSBTeW1ib2w7QWNjOk1HSToxMzE0ODg0XSIsIAoidmFzY3VsYXIgZW5kb3RoZWxpYWwgZ3Jvd3RoIGZhY3RvciBBIFtTb3VyY2U6TUdJIFN5bWJvbDtBY2M6TUdJOjEwMzE3OF0iLCAKInhhbnRoaW5lIGRlaHlkcm9nZW5hc2UgW1NvdXJjZTpNR0kgU3ltYm9sO0FjYzpNR0k6OTg5NzNdIiwgInppbmMgZmluZ2VyIENDQ0ggdHlwZSwgYW50aXZpcmFsIDEgW1NvdXJjZTpNR0kgU3ltYm9sO0FjYzpNR0k6MTkyNjAzMV0iLCAKInppbmMgZmluZ2VyIHByb3RlaW4sIG11bHRpdHlwZSAyIFtTb3VyY2U6TUdJIFN5bWJvbDtBY2M6TUdJOjEzMzQ0NDRdIgopLCBjbGFzcyA9ICJmYWN0b3IiKSwgZ2VuZV9iaW90eXBlID0gc3RydWN0dXJlKGMoMUwsIDFMLCAxTCwgCjFMLCAxTCwgMUwsIDFMLCAxTCwgMUwsIDFMLCAxTCwgMUwsIDFMLCAxTCwgMUwsIDFMLCAxTCwgMUwsIDFMLCAKMUwsIDFMLCAxTCwgMUwsIDFMLCAxTCwgMUwsIDFMLCAxTCwgMUwsIDFMLCAxTCwgMUwsIDFMLCAxTCwgMUwsIAoxTCwgMUwsIDFMLCAxTCwgMUwpLCAuTGFiZWwgPSAicHJvdGVpbl9jb2RpbmciLCBjbGFzcyA9ICJmYWN0b3IiKSksIHJvdy5uYW1lcyA9IGMoTkEsIAotNDBMKSwgLk5hbWVzID0gYygiZW5zZW1ibF9nZW5lX2lkIiwgImJhc2VNZWFuIiwgImxvZzJGb2xkQ2hhbmdlIiwgCiJsZmNTRSIsICJzdGF0IiwgInB2YWx1ZSIsICJwYWRqIiwgImdlbmVfc3ltYm9sIiwgImVudHJlemdlbmUiLCAKImRlc2NyaXB0aW9uIiwgImdlbmVfYmlvdHlwZSIpLCBjbGFzcyA9ICJkYXRhLmZyYW1lIikKYGBgCgpgYGB7cn0KaW5uYXRlREJfYnVsa191cF9nZW5lcyA8LSBpbm5hdGVEQl9nZW5lc19OU0NzX2J1bGtfdXBfdGFibGUgJT4lIGRwbHlyOjphcnJhbmdlKCBkZXNjKCBsb2cyRm9sZENoYW5nZSApKSAlPiUgdW5ncm91cCgpICU+JSBkcGx5cjo6c2VsZWN0KGVuc2VtYmxfZ2VuZV9pZCAsIGdlbmVfc3ltYm9sICkKCmlubmF0ZURCX2J1bGtfdXBfZ2VuZXMgPC0gaW5uYXRlREJfYnVsa191cF9nZW5lc1sxOjcsXQpgYGAKCldlIHNwbGl0IHRoZSBkZiBpbnRvIGEgbGlzdCAsIHJvd3MgYmVjb21lIGZpZWxkcwoKYGBge3J9CmdlbmVzX3VwX0lubmF0ZURCX3NtczJfYnVsa19saXN0IDwtIHNwbGl0KGlubmF0ZURCX2J1bGtfdXBfZ2VuZXMgLCBmID0gc2VxKG5yb3coaW5uYXRlREJfYnVsa191cF9nZW5lcykpKQpgYGAKCk5leHQgdXAgd2UgY2FsY3VsYXRlIHRoZSB0b3RhbFJlYWRzIGFuZCByZWFkcyBmb3Igb25seSB0aGUgc3BlY2lmaWVkIGdlbmUsIGFkZCB0aGUgaW5mb3JtYXRpb24gZm9yIGNlbGxzIHdoaWNoIGFnZSBhbmQgY2VsbHR5cGUgdGhleSBiZWxvbmcgdG8uCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpnZW5lc191cF9Jbm5hdGVEQl9kYXRhX2xpc3QgPC0gbGFwcGx5KGdlbmVzX3VwX0lubmF0ZURCX3NtczJfYnVsa19saXN0LCBmdW5jdGlvbih4KXsKICByYXdfY291bnRfZGF0YSA8LSBkYXRhLmZyYW1lKCB0b3RhbFJlYWRzID0gY29sU3VtcyhkYXRhXzEwWF9OU0NzWyxhcy5jaGFyYWN0ZXIoY2VsbHNfTlNDc18xMFgpXSkgLCBnZW5lUmVhZHMgPSBhcy5udW1lcmljKGRhdGFfMTBYX05TQ3NbIGFzLmNoYXJhY3Rlcih4JGVuc2VtYmxfZ2VuZV9pZCkgLGFzLmNoYXJhY3RlcihjZWxsc19OU0NzXzEwWCldKSAsIGdlbmUgPSBhcy5jaGFyYWN0ZXIoeCRnZW5lX3N5bWJvbCkgKSAgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigibmFtZSIpICU+JSBsZWZ0X2pvaW4oYW5ub18xMFhfTlNDcyAsIGJ5ID0gIm5hbWUiICkKCiAgdGFibGVfY2VsbHNfZXhwciA8LSBhZGRtYXJnaW5zKCB0YWJsZSggcmF3X2NvdW50X2RhdGEkYWdlICAsIHJhd19jb3VudF9kYXRhJGdlbmVSZWFkcyA+IDAgKSApCgogICMgZmlnLndpZHRoPTYgLCBmaWcuaGVpZ2h0PTIKCiAgZ2cgPC0gZ2dwbG90KGRhdGEgPSByYXdfY291bnRfZGF0YSAsIG1hcHBpbmcgPSBhZXMoeCA9IHRvdGFsUmVhZHMgLCB5ID0gZ2VuZVJlYWRzICkgKSArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gYWdlKSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygib2xkIiA9ICJzbGF0ZWJsdWUiICwgInlvdW5nIiA9ICJ5ZWxsb3dncmVlbiIpICkgKyBnZ3RpdGxlKHgkZ2VuZV9zeW1ib2wpCgpsaXN0KCAicmF3X2NvdW50X2RhdGEiID0gcmF3X2NvdW50X2RhdGEgLCAibnVtYmVyc19jZWxsc19leHByZXNzaW5nIiA9IHRhYmxlX2NlbGxzX2V4cHIgLCAicGxvdCIgPSBnZyAgKQp9KQpgYGAKCldlIGV4dHJhY3QgdGhlIHJhd19jb3VudHMgZGF0YSB0YWJsZSBmcm9tIHRoZSBsaXN0IHdlIGhhdmUganVzdCBjcmVhdGVkCgpgYGB7cn0KZ2VuZXNfdXBfSW5uYXRlREJfZGF0YV9saXN0X3Jhd19jb3VudHMgPC0gbGFwcGx5KGdlbmVzX3VwX0lubmF0ZURCX2RhdGFfbGlzdCwgZnVuY3Rpb24oeCl7IHgkcmF3X2NvdW50X2RhdGEgfSkKYGBgCgphbmQgYmluZCB0b2dldGhlciBhbGwgdGhlIGRhdGEgZnJhbWVzIGZvciB0aGUgZGlmZmVyZW50IGdlbmVzCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpnZW5lc191cF9Jbm5hdGVEQl9kYXRhX3Bsb3RfMTBYIDwtIGJpbmRfcm93cyhnZW5lc191cF9Jbm5hdGVEQl9kYXRhX2xpc3RfcmF3X2NvdW50cykKYGBgCgojIyMjIyBTY2F0dGVycGxvdAoKYGBge3J9CmdnX0lubmF0ZURCXzEwWCA8LSBnZ3Bsb3QoZGF0YSA9IGdlbmVzX3VwX0lubmF0ZURCX2RhdGFfcGxvdF8xMFggLCBtYXBwaW5nID0gYWVzKHggPSB0b3RhbFJlYWRzICwgeSA9IGdlbmVSZWFkcyApICkgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGFnZSkpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIm9sZCIgPSAic2xhdGVibHVlIiAsICJ5b3VuZyIgPSAieWVsbG93Z3JlZW4iKSApICsgZmFjZXRfZ3JpZCggZ2VuZSB+IC4gICkKYGBgCgpgYGB7ciBmaWcud2lkdGg9MTAgLCBmaWcuaGVpZ2h0PTIwfQpnZ19Jbm5hdGVEQl8xMFgKYGBgCgojIyMjIyBCYXJwbG90CgpTaG93IHRoZSBzYW1lIGRhdGEgYXMgc3RhY2tlZCBiYXJwbG90cwoKYGBge3J9CmdlbmVzX3VwX0lubmF0ZURCX2RhdGFfcGxvdF8xMFhfYmFycyA8LSBnZW5lc191cF9Jbm5hdGVEQl9kYXRhX3Bsb3RfMTBYICU+JSBtdXRhdGUoIGludGVydmFsID0gYmFzZTo6Y3V0KCBnZW5lUmVhZHMgLCBicmVha3MgPSBjKC0wLjEsIDAuNSw1LjUsNTAuNSxJbmYpICwgbGFiZWxzID0gYygiMCIsIjEtNSIsIjUtNTAiLCI1MC0iKSApICkgJT4lIGdyb3VwX2J5KGludGVydmFsLGdlbmUsYWdlKSAlPiUgc3VtbWFyaXNlKCBjb3VudHMgPSBuKCkgKSAlPiUgdW5ncm91cCgpICU+JSBtdXRhdGUoIGludGVydmFsID0gZmN0X3JldiggaW50ZXJ2YWwgKSAsIGFnZSA9IGZjdF9yZWNvZGUoIC5mID0gYWdlICwgICIyMiBtb250aCIgPSAib2xkIiwgICIyIG1vbnRoIiA9ICJ5b3VuZyIgKSAlPiUgZmN0X3JldigpICkgJT4lIGdyb3VwX2J5KGdlbmUsYWdlKSAlPiUgbXV0YXRlKCBQZXJjZW50ID0gMTAwKmNvdW50cy9zdW0oY291bnRzKSApCmBgYAoKYGBge3IgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NX0KZ2dwbG90KCBkYXRhID0gZ2VuZXNfdXBfSW5uYXRlREJfZGF0YV9wbG90XzEwWF9iYXJzICwgbWFwcGluZyA9IGFlcyh4ID0gYWdlICwgeSA9IFBlcmNlbnQgLCBmaWxsID0gaW50ZXJ2YWwpICkgKyBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKyBmYWNldF9ncmlkKGZhY2V0cyA9IC4gfiBnZW5lICkgKyB2aXJpZGlzOjpzY2FsZV9maWxsX3ZpcmlkaXMoIGRpc2NyZXRlID0gVFJVRSAsIGRpcmVjdGlvbiA9IC0xICwgZHJvcD1GQUxTRSAsICBuYW1lID0gIlJlYWQgY291bnQiICkgKyBnZ3RpdGxlKCJSZWFkIGNvdW50czogMTBYIikgKyB0aGVtZSggYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCxoanVzdCA9IDAsdmp1c3QgPSAwLjUpICwgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KSkgCmBgYAoKIyMjIyAKCgoKIyMgU01BUlRzZXEyCgpIb3cgbWFueSBjb3VudHMgZG8gZ2VuZXMgaGF2ZSBpbiB0aGUgU2luZ2xlIGNlbGwgZGF0YSBzZXRzLCB0aGF0IGFyZSBzaWduaWZpY2FudGx5IHVwcmVndWxhdGVkIGluIHRoZSBidWxrIE5TQ3MKCmBgYHtyfQpjZWxsc19OU0NzX1NNQVJUc2VxMiA8LSBhcy5jaGFyYWN0ZXIoYW5ub19TTUFSVHNlcTJfTlNDcyRuYW1lKQpgYGAKCiMjIyBJZml0MSAtIEVOU01VU0cwMDAwMDA3ODkyMAoKYGBge3J9CklmaXQxX2RmX1NNQVJUc2VxMiA8LSBkYXRhLmZyYW1lKCB0b3RhbFJlYWRzID0gY29sU3VtcyhkYXRhX1NNQVJUc2VxMl9OU0NzKSAsIGdlbmVSZWFkcyA9IGFzLm51bWVyaWMoZGF0YV9TTUFSVHNlcTJfTlNDc1siRU5TTVVTRzAwMDAwMDc4OTIwIixjZWxsc19OU0NzX1NNQVJUc2VxMl0pICwgZ2VuZSA9ICJJZml0MSIgKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJuYW1lIikgJT4lIGxlZnRfam9pbihhbm5vX1NNQVJUc2VxMl9OU0NzICwgYnkgPSAibmFtZSIgKQpgYGAKCmBgYHtyfQphZGRtYXJnaW5zKCB0YWJsZSggSWZpdDFfZGZfU01BUlRzZXEyJGFnZSAgLCBJZml0MV9kZl9TTUFSVHNlcTIkZ2VuZVJlYWRzID4gMCApICkKYGBgCgpgYGB7ciBmaWcud2lkdGg9NiAsIGZpZy5oZWlnaHQ9Mn0KZ2dfSWZpdDFfU01BUlRzZXEyIDwtIGdncGxvdChkYXRhID0gSWZpdDFfZGZfU01BUlRzZXEyICwgbWFwcGluZyA9IGFlcyh4ID0gdG90YWxSZWFkcyAsIHkgPSBnZW5lUmVhZHMgKSApICsgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBhZ2UpKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJvbGQiID0gInNsYXRlYmx1ZSIgLCAieW91bmciID0gInllbGxvd2dyZWVuIikgKQoKZ2dfSWZpdDFfU01BUlRzZXEyCmBgYAoKIyMjIFJzYWQyIC0gRU5TTVVTRzAwMDAwMDIwNjQxCgpgYGB7cn0KUnNhZDJfZGZfU01BUlRzZXEyIDwtIGRhdGEuZnJhbWUoIHRvdGFsUmVhZHMgPSBjb2xTdW1zKGRhdGFfU01BUlRzZXEyX05TQ3MpICwgZ2VuZVJlYWRzID0gYXMubnVtZXJpYyhkYXRhX1NNQVJUc2VxMl9OU0NzWyJFTlNNVVNHMDAwMDAwMjA2NDEiLGNlbGxzX05TQ3NfU01BUlRzZXEyXSkgLCBnZW5lID0gIlJzYWQyIiAgKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJuYW1lIikgJT4lIGxlZnRfam9pbihhbm5vX1NNQVJUc2VxMl9OU0NzICwgYnkgPSAibmFtZSIgKQpgYGAKCmBgYHtyfQphZGRtYXJnaW5zKCB0YWJsZSggUnNhZDJfZGZfU01BUlRzZXEyJGFnZSAgLCBSc2FkMl9kZl9TTUFSVHNlcTIkZ2VuZVJlYWRzID4gMCApICkKYGBgCgpgYGB7ciBmaWcud2lkdGg9NiAsIGZpZy5oZWlnaHQ9Mn0KZ2dfUnNhZDJfU01BUlRzZXEyIDwtIGdncGxvdChkYXRhID0gUnNhZDJfZGZfU01BUlRzZXEyICwgbWFwcGluZyA9IGFlcyh4ID0gdG90YWxSZWFkcyAsIHkgPSBnZW5lUmVhZHMgKSApICsgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBhZ2UpKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJvbGQiID0gInNsYXRlYmx1ZSIgLCAieW91bmciID0gInllbGxvd2dyZWVuIikgKQoKZ2dfUnNhZDJfU01BUlRzZXEyCmBgYAoKIyMjIEMzIC0gRU5TTVVTRzAwMDAwMDI0MTY0CgpgYGB7cn0KQzNfZGZfU01BUlRzZXEyIDwtIGRhdGEuZnJhbWUoIHRvdGFsUmVhZHMgPSBjb2xTdW1zKGRhdGFfU01BUlRzZXEyX05TQ3MpICwgZ2VuZVJlYWRzID0gYXMubnVtZXJpYyhkYXRhX1NNQVJUc2VxMl9OU0NzWyJFTlNNVVNHMDAwMDAwMjQxNjQiLGNlbGxzX05TQ3NfU01BUlRzZXEyXSkgLCBnZW5lID0gIkMzIiAgICkgICU+JSByb3duYW1lc190b19jb2x1bW4oIm5hbWUiKSAlPiUgbGVmdF9qb2luKGFubm9fU01BUlRzZXEyX05TQ3MgLCBieSA9ICJuYW1lIiApCmBgYAoKYGBge3J9CmFkZG1hcmdpbnMoIHRhYmxlKCBDM19kZl9TTUFSVHNlcTIkYWdlICAsIEMzX2RmX1NNQVJUc2VxMiRnZW5lUmVhZHMgPiAwICkgKQpgYGAKCmBgYHtyIGZpZy53aWR0aD02ICwgZmlnLmhlaWdodD0yfQpnZ19DM19TTUFSVHNlcTIgPC0gZ2dwbG90KGRhdGEgPSBDM19kZl9TTUFSVHNlcTIgLCBtYXBwaW5nID0gYWVzKHggPSB0b3RhbFJlYWRzICwgeSA9IGdlbmVSZWFkcyApICkgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGFnZSkpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIm9sZCIgPSAic2xhdGVibHVlIiAsICJ5b3VuZyIgPSAieWVsbG93Z3JlZW4iKSApCgpnZ19DM19TTUFSVHNlcTIKYGBgCgojIyMgSWlncDEgLSBFTlNNVVNHMDAwMDAwNTQwNzIKCmBgYHtyfQpJaWdwMV9kZl9TTUFSVHNlcTIgPC0gZGF0YS5mcmFtZSggdG90YWxSZWFkcyA9IGNvbFN1bXMoZGF0YV9TTUFSVHNlcTJfTlNDcykgLCBnZW5lUmVhZHMgPSBhcy5udW1lcmljKGRhdGFfU01BUlRzZXEyX05TQ3NbIkVOU01VU0cwMDAwMDA1NDA3MiIsY2VsbHNfTlNDc19TTUFSVHNlcTJdKSAsIGdlbmUgPSAiSWlncDEiICAgKSAgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigibmFtZSIpICU+JSBsZWZ0X2pvaW4oYW5ub19TTUFSVHNlcTJfTlNDcyAsIGJ5ID0gIm5hbWUiICkKYGBgCgpgYGB7cn0KYWRkbWFyZ2lucyggdGFibGUoIElpZ3AxX2RmX1NNQVJUc2VxMiRhZ2UgICwgSWlncDFfZGZfU01BUlRzZXEyJGdlbmVSZWFkcyA+IDAgKSApCmBgYAoKYGBge3IgZmlnLndpZHRoPTYgLCBmaWcuaGVpZ2h0PTJ9CmdnX0lpZ3AxX1NNQVJUc2VxMiA8LSBnZ3Bsb3QoZGF0YSA9IElpZ3AxX2RmX1NNQVJUc2VxMiAsIG1hcHBpbmcgPSBhZXMoeCA9IHRvdGFsUmVhZHMgLCB5ID0gZ2VuZVJlYWRzICkgKSArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gYWdlKSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygib2xkIiA9ICJzbGF0ZWJsdWUiICwgInlvdW5nIiA9ICJ5ZWxsb3dncmVlbiIpICkKCmdnX0lpZ3AxX1NNQVJUc2VxMgpgYGAKCgojIyMgSWZpNDQgLSBFTlNNVVNHMDAwMDAwMjgwMzcKCmBgYHtyfQpJZmk0NF9kZl9TTUFSVHNlcTIgPC0gZGF0YS5mcmFtZSggdG90YWxSZWFkcyA9IGNvbFN1bXMoZGF0YV9TTUFSVHNlcTJfTlNDcykgLCBnZW5lUmVhZHMgPSBhcy5udW1lcmljKGRhdGFfU01BUlRzZXEyX05TQ3NbIkVOU01VU0cwMDAwMDAyODAzNyIsY2VsbHNfTlNDc19TTUFSVHNlcTJdKSAsIGdlbmUgPSAiSWZpNDQiICAgKSAgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigibmFtZSIpICU+JSBsZWZ0X2pvaW4oYW5ub19TTUFSVHNlcTJfTlNDcyAsIGJ5ID0gIm5hbWUiICkKYGBgCgpgYGB7cn0KYWRkbWFyZ2lucyggdGFibGUoIElmaTQ0X2RmX1NNQVJUc2VxMiRhZ2UgICwgSWZpNDRfZGZfU01BUlRzZXEyJGdlbmVSZWFkcyA+IDAgKSApCmBgYAoKYGBge3IgZmlnLndpZHRoPTYgLCBmaWcuaGVpZ2h0PTJ9CmdnX0lmaTQ0X1NNQVJUc2VxMiA8LSBnZ3Bsb3QoZGF0YSA9IElmaTQ0X2RmX1NNQVJUc2VxMiAsIG1hcHBpbmcgPSBhZXMoeCA9IHRvdGFsUmVhZHMgLCB5ID0gZ2VuZVJlYWRzICkgKSArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gYWdlKSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygib2xkIiA9ICJzbGF0ZWJsdWUiICwgInlvdW5nIiA9ICJ5ZWxsb3dncmVlbiIpICkKCmdnX0lmaTQ0X1NNQVJUc2VxMgpgYGAKCgojIyMgQ3hjbDEwIC0gRU5TTVVTRzAwMDAwMDM0ODU1CgpgYGB7cn0KQ3hjbDEwX2RmX1NNQVJUc2VxMiA8LSBkYXRhLmZyYW1lKCB0b3RhbFJlYWRzID0gY29sU3VtcyhkYXRhX1NNQVJUc2VxMl9OU0NzKSAsIGdlbmVSZWFkcyA9IGFzLm51bWVyaWMoZGF0YV9TTUFSVHNlcTJfTlNDc1siRU5TTVVTRzAwMDAwMDM0ODU1IixjZWxsc19OU0NzX1NNQVJUc2VxMl0pICwgZ2VuZSA9ICJDeGNsMTAiICAgKSAgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigibmFtZSIpICU+JSBsZWZ0X2pvaW4oYW5ub19TTUFSVHNlcTJfTlNDcyAsIGJ5ID0gIm5hbWUiICkKYGBgCmBgYHtyfQphZGRtYXJnaW5zKCB0YWJsZSggQ3hjbDEwX2RmX1NNQVJUc2VxMiRhZ2UgICwgQ3hjbDEwX2RmX1NNQVJUc2VxMiRnZW5lUmVhZHMgPiAwICkgKQpgYGAKCmBgYHtyIGZpZy53aWR0aD02ICwgZmlnLmhlaWdodD0yfQpnZ19DeGNsMTBfU01BUlRzZXEyIDwtIGdncGxvdChkYXRhID0gQ3hjbDEwX2RmX1NNQVJUc2VxMiAsIG1hcHBpbmcgPSBhZXMoeCA9IHRvdGFsUmVhZHMgLCB5ID0gZ2VuZVJlYWRzICkgKSArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gYWdlKSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygib2xkIiA9ICJzbGF0ZWJsdWUiICwgInlvdW5nIiA9ICJ5ZWxsb3dncmVlbiIpICkKCmdnX0N4Y2wxMF9TTUFSVHNlcTIKYGBgCgojIyMgSWwzMyAtIEVOU01VU0cwMDAwMDAyNDgxMAoKYGBge3J9CklsMzNfZGZfU01BUlRzZXEyIDwtIGRhdGEuZnJhbWUoIHRvdGFsUmVhZHMgPSBjb2xTdW1zKGRhdGFfU01BUlRzZXEyX05TQ3MpICwgZ2VuZVJlYWRzID0gYXMubnVtZXJpYyhkYXRhX1NNQVJUc2VxMl9OU0NzWyJFTlNNVVNHMDAwMDAwMjQ4MTAiLGNlbGxzX05TQ3NfU01BUlRzZXEyXSkgLCBnZW5lID0gIklsMzMiICAgKSAgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigibmFtZSIpICU+JSBsZWZ0X2pvaW4oYW5ub19TTUFSVHNlcTJfTlNDcyAsIGJ5ID0gIm5hbWUiICkKYGBgCgpgYGB7cn0KYWRkbWFyZ2lucyggdGFibGUoIElsMzNfZGZfU01BUlRzZXEyJGFnZSAgLCBJbDMzX2RmX1NNQVJUc2VxMiRnZW5lUmVhZHMgPiAwICkgKQpgYGAKCmBgYHtyIGZpZy53aWR0aD02ICwgZmlnLmhlaWdodD0yfQpnZ19JbDMzX1NNQVJUc2VxMiA8LSBnZ3Bsb3QoZGF0YSA9IElsMzNfZGZfU01BUlRzZXEyICwgbWFwcGluZyA9IGFlcyh4ID0gdG90YWxSZWFkcyAsIHkgPSBnZW5lUmVhZHMgKSApICsgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBhZ2UpKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJvbGQiID0gInNsYXRlYmx1ZSIgLCAieW91bmciID0gInllbGxvd2dyZWVuIikgKQoKZ2dfSWwzM19TTUFSVHNlcTIKYGBgCgojIyMgRGR4NjAgLSBFTlNNVVNHMDAwMDAwMzc5MjEKCmBgYHtyfQpEZHg2MF9kZl9TTUFSVHNlcTIgPC0gZGF0YS5mcmFtZSggdG90YWxSZWFkcyA9IGNvbFN1bXMoZGF0YV9TTUFSVHNlcTJfTlNDcykgLCBnZW5lUmVhZHMgPSBhcy5udW1lcmljKGRhdGFfU01BUlRzZXEyX05TQ3NbIkVOU01VU0cwMDAwMDAzNzkyMSIsY2VsbHNfTlNDc19TTUFSVHNlcTJdKSAsIGdlbmUgPSAiRGR4NjAiICAgKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJuYW1lIikgJT4lIGxlZnRfam9pbihhbm5vX1NNQVJUc2VxMl9OU0NzICwgYnkgPSAibmFtZSIgKQpgYGAKCmBgYHtyfQphZGRtYXJnaW5zKCB0YWJsZSggRGR4NjBfZGZfU01BUlRzZXEyJGFnZSAgLCBEZHg2MF9kZl9TTUFSVHNlcTIkZ2VuZVJlYWRzID4gMCApICkKYGBgCgpgYGB7ciBmaWcud2lkdGg9NiAsIGZpZy5oZWlnaHQ9Mn0KZ2dfRGR4NjBfU01BUlRzZXEyIDwtIGdncGxvdChkYXRhID0gRGR4NjBfZGZfU01BUlRzZXEyICwgbWFwcGluZyA9IGFlcyh4ID0gdG90YWxSZWFkcyAsIHkgPSBnZW5lUmVhZHMgKSApICsgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBhZ2UpKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJvbGQiID0gInNsYXRlYmx1ZSIgLCAieW91bmciID0gInllbGxvd2dyZWVuIikgKQoKZ2dfRGR4NjBfU01BUlRzZXEyCmBgYAoKIyMjIElmaTQ3IC0gRU5TTVVTRzAwMDAwMDc4OTIwCgpgYGB7cn0KSWZpNDdfZGZfU01BUlRzZXEyIDwtIGRhdGEuZnJhbWUoIHRvdGFsUmVhZHMgPSBjb2xTdW1zKGRhdGFfU01BUlRzZXEyX05TQ3MpICwgZ2VuZVJlYWRzID0gYXMubnVtZXJpYyhkYXRhX1NNQVJUc2VxMl9OU0NzWyJFTlNNVVNHMDAwMDAwNzg5MjAiLGNlbGxzX05TQ3NfU01BUlRzZXEyXSkgLCBnZW5lID0gIklmaTQ3IiAgICkgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigibmFtZSIpICU+JSBsZWZ0X2pvaW4oYW5ub19TTUFSVHNlcTJfTlNDcyAsIGJ5ID0gIm5hbWUiICkKYGBgCgpgYGB7cn0KYWRkbWFyZ2lucyggdGFibGUoIElmaTQ3X2RmX1NNQVJUc2VxMiRhZ2UgICwgSWZpNDdfZGZfU01BUlRzZXEyJGdlbmVSZWFkcyA+IDAgKSApCmBgYAoKYGBge3IgZmlnLndpZHRoPTYgLCBmaWcuaGVpZ2h0PTJ9CmdnX0lmaTQ3X1NNQVJUc2VxMiA8LSBnZ3Bsb3QoZGF0YSA9IElmaTQ3X2RmX1NNQVJUc2VxMiAsIG1hcHBpbmcgPSBhZXMoeCA9IHRvdGFsUmVhZHMgLCB5ID0gZ2VuZVJlYWRzICkgKSArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gYWdlKSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygib2xkIiA9ICJzbGF0ZWJsdWUiICwgInlvdW5nIiA9ICJ5ZWxsb3dncmVlbiIpICkKCmdnX0lmaTQ3X1NNQVJUc2VxMgpgYGAKCiMjIyBHbTQ5NTEgLSBFTlNNVVNHMDAwMDAwNzM1NTUKCmBgYHtyfQpHbTQ5NTFfZGZfU01BUlRzZXEyIDwtIGRhdGEuZnJhbWUoIHRvdGFsUmVhZHMgPSBjb2xTdW1zKGRhdGFfU01BUlRzZXEyX05TQ3MpICwgZ2VuZVJlYWRzID0gYXMubnVtZXJpYyhkYXRhX1NNQVJUc2VxMl9OU0NzWyJFTlNNVVNHMDAwMDAwNzM1NTUiLGNlbGxzX05TQ3NfU01BUlRzZXEyXSkgLCBnZW5lID0gIkdtNDk1MSIgICApICU+JSByb3duYW1lc190b19jb2x1bW4oIm5hbWUiKSAlPiUgbGVmdF9qb2luKGFubm9fU01BUlRzZXEyX05TQ3MgLCBieSA9ICJuYW1lIiApCmBgYAoKYGBge3J9CmFkZG1hcmdpbnMoIHRhYmxlKCBHbTQ5NTFfZGZfU01BUlRzZXEyJGFnZSAgLCBHbTQ5NTFfZGZfU01BUlRzZXEyJGdlbmVSZWFkcyA+IDAgKSApCmBgYAoKYGBge3IgZmlnLndpZHRoPTYgLCBmaWcuaGVpZ2h0PTJ9CmdnX0dtNDk1MV9TTUFSVHNlcTIgPC0gZ2dwbG90KGRhdGEgPSBHbTQ5NTFfZGZfU01BUlRzZXEyICwgbWFwcGluZyA9IGFlcyh4ID0gdG90YWxSZWFkcyAsIHkgPSBnZW5lUmVhZHMgKSApICsgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBhZ2UpKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJvbGQiID0gInNsYXRlYmx1ZSIgLCAieW91bmciID0gInllbGxvd2dyZWVuIikgKQoKZ2dfR200OTUxX1NNQVJUc2VxMgpgYGAKCiMjIyBTbGMxYTMgLSBFTlNNVVNHMDAwMDAwMDUzNjAKCmBgYHtyfQpTbGMxYTNfZGZfU01BUlRzZXEyIDwtIGRhdGEuZnJhbWUoIHRvdGFsUmVhZHMgPSBjb2xTdW1zKGRhdGFfU01BUlRzZXEyX05TQ3MpICwgZ2VuZVJlYWRzID0gYXMubnVtZXJpYyhkYXRhX1NNQVJUc2VxMl9OU0NzWyJFTlNNVVNHMDAwMDAwMDUzNjAiLGNlbGxzX05TQ3NfU01BUlRzZXEyXSkgLCBnZW5lID0gIlNsYzFhMyIgICApICU+JSByb3duYW1lc190b19jb2x1bW4oIm5hbWUiKSAlPiUgbGVmdF9qb2luKGFubm9fU01BUlRzZXEyX05TQ3MgLCBieSA9ICJuYW1lIiApCmBgYAoKYGBge3J9CmFkZG1hcmdpbnMoIHRhYmxlKCBTbGMxYTNfZGZfU01BUlRzZXEyJGFnZSAgLCBTbGMxYTNfZGZfU01BUlRzZXEyJGdlbmVSZWFkcyA+IDAgKSApCmBgYAoKYGBge3IgZmlnLndpZHRoPTYgLCBmaWcuaGVpZ2h0PTJ9CmdnX1NsYzFhM19TTUFSVHNlcTIgPC0gZ2dwbG90KGRhdGEgPSBTbGMxYTNfZGZfU01BUlRzZXEyICwgbWFwcGluZyA9IGFlcyh4ID0gdG90YWxSZWFkcyAsIHkgPSBnZW5lUmVhZHMgKSApICsgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBhZ2UpKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJvbGQiID0gInNsYXRlYmx1ZSIgLCAieW91bmciID0gInllbGxvd2dyZWVuIikgKQoKZ2dfU2xjMWEzX1NNQVJUc2VxMgpgYGAKCiMjIyBTb3gyIC0gRU5TTVVTRzAwMDAwMDc0NjM3CgpgYGB7cn0KU294Ml9kZl9TTUFSVHNlcTIgPC0gZGF0YS5mcmFtZSggdG90YWxSZWFkcyA9IGNvbFN1bXMoZGF0YV9TTUFSVHNlcTJfTlNDcykgLCBnZW5lUmVhZHMgPSBhcy5udW1lcmljKGRhdGFfU01BUlRzZXEyX05TQ3NbIkVOU01VU0cwMDAwMDA3NDYzNyIsY2VsbHNfTlNDc19TTUFSVHNlcTJdKSAsIGdlbmUgPSAiU294MiIgICApICU+JSByb3duYW1lc190b19jb2x1bW4oIm5hbWUiKSAlPiUgbGVmdF9qb2luKGFubm9fU01BUlRzZXEyX05TQ3MgLCBieSA9ICJuYW1lIiApCmBgYAoKYGBge3J9CmFkZG1hcmdpbnMoIHRhYmxlKCBTb3gyX2RmX1NNQVJUc2VxMiRhZ2UgICwgU294Ml9kZl9TTUFSVHNlcTIkZ2VuZVJlYWRzID4gMCApICkKYGBgCgoKYGBge3IgZmlnLndpZHRoPTYgLCBmaWcuaGVpZ2h0PTJ9CmdnX1NveDJfU01BUlRzZXEyIDwtIGdncGxvdChkYXRhID0gU294Ml9kZl9TTUFSVHNlcTIgLCBtYXBwaW5nID0gYWVzKHggPSB0b3RhbFJlYWRzICwgeSA9IGdlbmVSZWFkcyApICkgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGFnZSkpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIm9sZCIgPSAic2xhdGVibHVlIiAsICJ5b3VuZyIgPSAieWVsbG93Z3JlZW4iKSApCgpnZ19Tb3gyX1NNQVJUc2VxMgpgYGAKCiMjIyBDb21iaW5lIGFsbCBnZW5lcwoKYGBge3J9CmludGVyZmVyb25fZ2VuZXNfYnVsa19kZl9TTUFSVHNlcTIgPC0gYmluZF9yb3dzKCBJZml0MV9kZl9TTUFSVHNlcTIgLCBDM19kZl9TTUFSVHNlcTIgLCBSc2FkMl9kZl9TTUFSVHNlcTIgLCBJaWdwMV9kZl9TTUFSVHNlcTIgLCBJZmk0N19kZl9TTUFSVHNlcTIgLCBJbDMzX2RmX1NNQVJUc2VxMiAsIEN4Y2wxMF9kZl9TTUFSVHNlcTIgLCBTb3gyX2RmX1NNQVJUc2VxMiAsIFNsYzFhM19kZl9TTUFSVHNlcTIgKQpgYGAKCiMjIyMgU2NhdHRlcnBsb3QKCmBgYHtyfQpnZ19pbnRlcmZlcm9uX2dlbmVzX1NNQVJUc2VxMiA8LSBnZ3Bsb3QoZGF0YSA9IGludGVyZmVyb25fZ2VuZXNfYnVsa19kZl9TTUFSVHNlcTIgLCBtYXBwaW5nID0gYWVzKHggPSB0b3RhbFJlYWRzICwgeSA9IGdlbmVSZWFkcyApICkgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGFnZSkpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIm9sZCIgPSAic2xhdGVibHVlIiAsICJ5b3VuZyIgPSAieWVsbG93Z3JlZW4iKSApICsgZmFjZXRfZ3JpZCggZ2VuZSB+IC4gKSArIHNjYWxlX3lfbG9nMTAoKSArIHlsYWIoImxvZzEwKCBnZW5lUmVhZHMgKSIpCmBgYAoKCmBgYHtyIGZpZy53aWR0aD0xMCAsIGZpZy5oZWlnaHQ9MTJ9CmdnX2ludGVyZmVyb25fZ2VuZXNfU01BUlRzZXEyCmBgYAoKIyMjIyBCYXJwbG90CgpTaG93IHRoZSBzYW1lIGRhdGEgYXMgc3RhY2tlZCBiYXJwbG90cwoKYGBge3J9CmludGVyZmVyb25fZ2VuZXNfYnVsa19kZl9TTUFSVHNlcTJfYmFycyA8LSBpbnRlcmZlcm9uX2dlbmVzX2J1bGtfZGZfU01BUlRzZXEyICU+JSBtdXRhdGUoIGludGVydmFsID0gYmFzZTo6Y3V0KCBnZW5lUmVhZHMgLCBicmVha3MgPSBjKC0wLjEsIDAuNSw1LjUsNTAuNSxJbmYpICwgbGFiZWxzID0gYygiMCIsIjEtNSIsIjUtNTAiLCI1MC0iKSApICkgJT4lIGdyb3VwX2J5KGludGVydmFsLGdlbmUsYWdlKSAlPiUgc3VtbWFyaXNlKCBjb3VudHMgPSBuKCkgKSAlPiUgdW5ncm91cCgpICU+JSBtdXRhdGUoIGludGVydmFsID0gZmN0X3JldiggaW50ZXJ2YWwgKSAsIGFnZSA9IGZjdF9yZWNvZGUoIC5mID0gYWdlICwgICIyMiBtb250aCIgPSAib2xkIiwgICIyIG1vbnRoIiA9ICJ5b3VuZyIgKSAlPiUgZmN0X3JldigpICkgJT4lIGdyb3VwX2J5KGdlbmUsYWdlKSAlPiUgbXV0YXRlKCBQZXJjZW50ID0gMTAwKmNvdW50cy9zdW0oY291bnRzKSApCmBgYAoKYGBge3IgZmlnLndpZHRoPTE4LCBmaWcuaGVpZ2h0PTV9CmdncGxvdCggZGF0YSA9IGludGVyZmVyb25fZ2VuZXNfYnVsa19kZl9TTUFSVHNlcTJfYmFycyAsIG1hcHBpbmcgPSBhZXMoeCA9IGFnZSAsIHkgPSBQZXJjZW50ICwgZmlsbCA9IGludGVydmFsKSApICsgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsgZmFjZXRfZ3JpZChmYWNldHMgPSAuIH4gZ2VuZSApICsgdmlyaWRpczo6c2NhbGVfZmlsbF92aXJpZGlzKCBkaXNjcmV0ZSA9IFRSVUUgLCBkaXJlY3Rpb24gPSAtMSApICsgZ2d0aXRsZSgiUmVhZCBjb3VudHM6IFNNQVJUc2VxMiIpCmBgYAoKCiMjIyBCdWxrIE5TQyBnZW5lcyBpbiBJbm5hdGVEQgoKV2hpY2ggZ2VuZXMgYXJlIHRoZSBvbmVzIHdpdGggaGlnaCBmb2xkIGNoYW5nZXMgaW4gdGhlIGJ1bGsgYW5kIGFyZSBhbm5vdGF0ZWQgaW4gSW5uYXRlREIKCmBgYHtyfQppbm5hdGVEQl9idWxrX3VwX2dlbmVzIDwtIGlubmF0ZURCX2dlbmVzX05TQ3NfYnVsa191cF90YWJsZSAlPiUgZHBseXI6OmFycmFuZ2UoIGRlc2MoIGxvZzJGb2xkQ2hhbmdlICkpICU+JSB1bmdyb3VwKCkgJT4lIGRwbHlyOjpzZWxlY3QoZW5zZW1ibF9nZW5lX2lkICwgZ2VuZV9zeW1ib2wgKQoKaW5uYXRlREJfYnVsa191cF9nZW5lcyA8LSBpbm5hdGVEQl9idWxrX3VwX2dlbmVzWzE6NyxdCmBgYAoKV2Ugc3BsaXQgdGhlIGRmIGludG8gYSBsaXN0ICwgcm93cyBiZWNvbWUgZmllbGRzCgpgYGB7cn0KZ2VuZXNfdXBfSW5uYXRlREJfc21zMl9idWxrX2xpc3QgPC0gc3BsaXQoaW5uYXRlREJfYnVsa191cF9nZW5lcyAsIGYgPSBzZXEobnJvdyhpbm5hdGVEQl9idWxrX3VwX2dlbmVzKSkpCmBgYAoKTmV4dCB1cCB3ZSBjYWxjdWxhdGUgdGhlIHRvdGFsUmVhZHMgYW5kIHJlYWRzIGZvciBvbmx5IHRoZSBzcGVjaWZpZWQgZ2VuZSwgYWRkIHRoZSBpbmZvcm1hdGlvbiBmb3IgY2VsbHMgd2hpY2ggYWdlIGFuZCBjZWxsdHlwZSB0aGV5IGJlbG9uZyB0by4KCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmdlbmVzX3VwX0lubmF0ZURCX2RhdGFfbGlzdF9TTUFSVHNlcTIgPC0gbGFwcGx5KGdlbmVzX3VwX0lubmF0ZURCX3NtczJfYnVsa19saXN0LCBmdW5jdGlvbih4KXsKICByYXdfY291bnRfZGF0YSA8LSBkYXRhLmZyYW1lKCB0b3RhbFJlYWRzID0gY29sU3VtcyhkYXRhX1NNQVJUc2VxMl9OU0NzWyxhcy5jaGFyYWN0ZXIoY2VsbHNfTlNDc19TTUFSVHNlcTIpXSkgLCBnZW5lUmVhZHMgPSBhcy5udW1lcmljKGRhdGFfU01BUlRzZXEyX05TQ3NbIGFzLmNoYXJhY3Rlcih4JGVuc2VtYmxfZ2VuZV9pZCkgLGFzLmNoYXJhY3RlcihjZWxsc19OU0NzX1NNQVJUc2VxMikgXSkgLCBnZW5lID0gYXMuY2hhcmFjdGVyKCB4JGdlbmVfc3ltYm9sICkgKSAgJT4lIHJvd25hbWVzX3RvX2NvbHVtbigibmFtZSIpICU+JSBsZWZ0X2pvaW4oYW5ub19TTUFSVHNlcTJfTlNDcyAsIGJ5ID0gIm5hbWUiICkKCiAgdGFibGVfY2VsbHNfZXhwciA8LSBhZGRtYXJnaW5zKCB0YWJsZSggcmF3X2NvdW50X2RhdGEkYWdlICAsIHJhd19jb3VudF9kYXRhJGdlbmVSZWFkcyA+IDAgKSApCgogICMgZmlnLndpZHRoPTYgLCBmaWcuaGVpZ2h0PTIKCiAgZ2cgPC0gZ2dwbG90KGRhdGEgPSByYXdfY291bnRfZGF0YSAsIG1hcHBpbmcgPSBhZXMoeCA9IHRvdGFsUmVhZHMgLCB5ID0gZ2VuZVJlYWRzICkgKSArIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gYWdlKSkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygib2xkIiA9ICJzbGF0ZWJsdWUiICwgInlvdW5nIiA9ICJ5ZWxsb3dncmVlbiIpICkgKyBnZ3RpdGxlKHgkZ2VuZV9zeW1ib2wpCgpsaXN0KCAicmF3X2NvdW50X2RhdGEiID0gcmF3X2NvdW50X2RhdGEgLCAibnVtYmVyc19jZWxsc19leHByZXNzaW5nIiA9IHRhYmxlX2NlbGxzX2V4cHIgLCAicGxvdCIgPSBnZyAgKQp9KQpgYGAKCldlIGV4dHJhY3QgdGhlIHJhd19jb3VudHMgZGF0YSB0YWJsZSBmcm9tIHRoZSBsaXN0IHdlIGhhdmUganVzdCBjcmVhdGVkCgpgYGB7cn0KZ2VuZXNfdXBfSW5uYXRlREJfZGF0YV9saXN0X1NNQVJUc2VxMl9yYXdfY291bnRzIDwtIGxhcHBseShnZW5lc191cF9Jbm5hdGVEQl9kYXRhX2xpc3RfU01BUlRzZXEyLCBmdW5jdGlvbih4KXsgeCRyYXdfY291bnRfZGF0YSB9KQpgYGAKCmFuZCBiaW5kIHRvZ2V0aGVyIGFsbCB0aGUgZGF0YSBmcmFtZXMgZm9yIHRoZSBkaWZmZXJlbnQgZ2VuZXMKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmdlbmVzX3VwX0lubmF0ZURCX2RhdGFfcGxvdF9TTUFSVHNlcTIgPC0gYmluZF9yb3dzKGdlbmVzX3VwX0lubmF0ZURCX2RhdGFfbGlzdF9TTUFSVHNlcTJfcmF3X2NvdW50cykKYGBgCgojIyMjIyBTY2F0dGVycGxvdAoKYGBge3J9CmdnX0lubmF0ZURCX1NNQVJUc2VxMiA8LSBnZ3Bsb3QoZGF0YSA9IGdlbmVzX3VwX0lubmF0ZURCX2RhdGFfcGxvdF9TTUFSVHNlcTIgLCBtYXBwaW5nID0gYWVzKHggPSB0b3RhbFJlYWRzICwgeSA9IGdlbmVSZWFkcyApICkgKyBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGFnZSkpICsgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIm9sZCIgPSAic2xhdGVibHVlIiAsICJ5b3VuZyIgPSAieWVsbG93Z3JlZW4iKSApICsgZmFjZXRfZ3JpZCggZ2VuZSB+IC4gICkKYGBgCgpgYGB7ciBmaWcud2lkdGg9MTAgLCBmaWcuaGVpZ2h0PTIwfQpnZ19Jbm5hdGVEQl9TTUFSVHNlcTIKYGBgCgojIyMjIyBCYXJwbG90CgpTaG93IHRoZSBzYW1lIGRhdGEgYXMgc3RhY2tlZCBiYXJwbG90cwoKYGBge3J9CmdlbmVzX3VwX0lubmF0ZURCX2RhdGFfcGxvdF9TTUFSVHNlcTJfYmFycyA8LSBnZW5lc191cF9Jbm5hdGVEQl9kYXRhX3Bsb3RfU01BUlRzZXEyICU+JSBtdXRhdGUoIGludGVydmFsID0gYmFzZTo6Y3V0KCBnZW5lUmVhZHMgLCBicmVha3MgPSBjKC0wLjEsIDAuNSw1LjUsNTAuNSxJbmYpICwgbGFiZWxzID0gYygiMCIsIjEtNSIsIjUtNTAiLCI1MC0iKSApICkgJT4lIGdyb3VwX2J5KGludGVydmFsLGdlbmUsYWdlKSAlPiUgc3VtbWFyaXNlKCBjb3VudHMgPSBuKCkgKSAlPiUgdW5ncm91cCgpICU+JSBtdXRhdGUoIGludGVydmFsID0gZmN0X3JldiggaW50ZXJ2YWwgKSAsIGFnZSA9IGZjdF9yZWNvZGUoIC5mID0gYWdlICwgICIyMiBtb250aCIgPSAib2xkIiwgICIyIG1vbnRoIiA9ICJ5b3VuZyIgKSAlPiUgZmN0X3JldigpICkgJT4lIGdyb3VwX2J5KGdlbmUsYWdlKSAlPiUgbXV0YXRlKCBQZXJjZW50ID0gMTAwKmNvdW50cy9zdW0oY291bnRzKSApCmBgYAoKYGBge3IgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NX0KZ2dwbG90KCBkYXRhID0gZ2VuZXNfdXBfSW5uYXRlREJfZGF0YV9wbG90X1NNQVJUc2VxMl9iYXJzICwgbWFwcGluZyA9IGFlcyh4ID0gYWdlICwgeSA9IFBlcmNlbnQgLCBmaWxsID0gaW50ZXJ2YWwpICkgKyBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKyBmYWNldF9ncmlkKGZhY2V0cyA9IC4gfiBnZW5lICkgKyB2aXJpZGlzOjpzY2FsZV9maWxsX3ZpcmlkaXMoIGRpc2NyZXRlID0gVFJVRSAsIGRyb3A9RkFMU0UgLCBkaXJlY3Rpb24gPSAtMSAsIG5hbWUgPSAiUmVhZCBjb3VudCIgKSArIGdndGl0bGUoIlJlYWQgY291bnRzOiBTTUFSVHNlcTIiKSArIHRoZW1lKCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLGhqdXN0ID0gMCx2anVzdCA9IDAuNSkpCmBgYAoKCiMjIENvbWJpbmVkIEJhcnBsb3RzIGZvciAxMFggYW5kIFNNQVJUc2VxMgoKYGBge3J9CmdlbmVzX3VwX0lubmF0ZURCX2RhdGFfYmFycGxvdCA8LSBiaW5kX3Jvd3MoCiAgZHBseXI6Om11dGF0ZSggZ2VuZXNfdXBfSW5uYXRlREJfZGF0YV9wbG90X1NNQVJUc2VxMl9iYXJzICwgc2VxbWV0aG9kID0gIlNtYXJ0LVNlcTIiICkgLCAKICBkcGx5cjo6bXV0YXRlKCBnZW5lc191cF9Jbm5hdGVEQl9kYXRhX3Bsb3RfMTBYX2JhcnMgICwgc2VxbWV0aG9kID0gIjEwWCIgKSAKKQpgYGAKCgpgYGB7ciBmaWcud2lkdGg9NC4xICwgZmlnLmhlaWdodD02fQppZHggPSAxCgpnZzEgPC0gZ2dwbG90KCBkYXRhID0gZ2VuZXNfdXBfSW5uYXRlREJfZGF0YV9iYXJwbG90ICU+JSBkcGx5cjo6ZmlsdGVyKCBnZW5lID09IGlubmF0ZURCX2J1bGtfdXBfZ2VuZXMkZ2VuZV9zeW1ib2xbW2lkeF1dICkgLCBtYXBwaW5nID0gYWVzKHggPSBhZ2UgLCB5ID0gUGVyY2VudCAsIGZpbGwgPSBpbnRlcnZhbCkgKSArIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArIGZhY2V0X2dyaWQoZmFjZXRzID0gLiB+IHNlcW1ldGhvZCApICsgdmlyaWRpczo6c2NhbGVfZmlsbF92aXJpZGlzKCBkaXNjcmV0ZSA9IFRSVUUgLCBkcm9wPUZBTFNFICwgZGlyZWN0aW9uID0gLTEgLCBuYW1lID0gIlJlYWQgY291bnQiICkgKyBnZ3RpdGxlKCBwYXN0ZSgiUmVhZCBjb3VudHM6IiwgaW5uYXRlREJfYnVsa191cF9nZW5lcyRnZW5lX3N5bWJvbFtbaWR4XV0gKSApICsgdGhlbWUoIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsaGp1c3QgPSAwLHZqdXN0ID0gMC41KSkKCmdnMQpgYGAKCmBgYHtyICBmaWcud2lkdGg9NC4xICwgZmlnLmhlaWdodD02fQppZHggPSAyCgogZ2cyIDwtIGdncGxvdCggZGF0YSA9IGdlbmVzX3VwX0lubmF0ZURCX2RhdGFfYmFycGxvdCAlPiUgZHBseXI6OmZpbHRlciggZ2VuZSA9PSBpbm5hdGVEQl9idWxrX3VwX2dlbmVzJGdlbmVfc3ltYm9sW2lkeF0gKSAsIG1hcHBpbmcgPSBhZXMoeCA9IGFnZSAsIHkgPSBQZXJjZW50ICwgZmlsbCA9IGludGVydmFsKSApICsgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsgZmFjZXRfZ3JpZChmYWNldHMgPSAuIH4gc2VxbWV0aG9kICkgKyB2aXJpZGlzOjpzY2FsZV9maWxsX3ZpcmlkaXMoIGRpc2NyZXRlID0gVFJVRSAsIGRyb3A9RkFMU0UgLCBkaXJlY3Rpb24gPSAtMSAsIG5hbWUgPSAiUmVhZCBjb3VudCIgKSArIGdndGl0bGUoIHBhc3RlKCJSZWFkIGNvdW50czoiLCBpbm5hdGVEQl9idWxrX3VwX2dlbmVzJGdlbmVfc3ltYm9sW1tpZHhdXSApICkgKyB0aGVtZSggYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCxoanVzdCA9IDAsdmp1c3QgPSAwLjUpKQogCiBnZzIKYGBgCgpgYGB7ciAgZmlnLndpZHRoPTQuMSAsIGZpZy5oZWlnaHQ9Nn0KaWR4ID0gMwoKZ2czIDwtIGdncGxvdCggZGF0YSA9IGdlbmVzX3VwX0lubmF0ZURCX2RhdGFfYmFycGxvdCAlPiUgZHBseXI6OmZpbHRlciggZ2VuZSA9PSBpbm5hdGVEQl9idWxrX3VwX2dlbmVzJGdlbmVfc3ltYm9sW2lkeF0gKSAsIG1hcHBpbmcgPSBhZXMoeCA9IGFnZSAsIHkgPSBQZXJjZW50ICwgZmlsbCA9IGludGVydmFsKSApICsgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsgZmFjZXRfZ3JpZChmYWNldHMgPSAuIH4gc2VxbWV0aG9kICkgKyB2aXJpZGlzOjpzY2FsZV9maWxsX3ZpcmlkaXMoIGRpc2NyZXRlID0gVFJVRSAsIGRyb3A9RkFMU0UgLCBkaXJlY3Rpb24gPSAtMSAsIG5hbWUgPSAiUmVhZCBjb3VudCIgKSArIGdndGl0bGUoIHBhc3RlKCJSZWFkIGNvdW50czoiLCBpbm5hdGVEQl9idWxrX3VwX2dlbmVzJGdlbmVfc3ltYm9sW1tpZHhdXSApICkgKyB0aGVtZSggYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCxoanVzdCA9IDAsdmp1c3QgPSAwLjUpKQoKZ2czCmBgYAoKYGBge3IgIGZpZy53aWR0aD00LjEgLCBmaWcuaGVpZ2h0PTZ9CmlkeCA9IDQKCmdnNCA8LSBnZ3Bsb3QoIGRhdGEgPSBnZW5lc191cF9Jbm5hdGVEQl9kYXRhX2JhcnBsb3QgJT4lIGRwbHlyOjpmaWx0ZXIoIGdlbmUgPT0gaW5uYXRlREJfYnVsa191cF9nZW5lcyRnZW5lX3N5bWJvbFtpZHhdICkgLCBtYXBwaW5nID0gYWVzKHggPSBhZ2UgLCB5ID0gUGVyY2VudCAsIGZpbGwgPSBpbnRlcnZhbCkgKSArIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArIGZhY2V0X2dyaWQoZmFjZXRzID0gLiB+IHNlcW1ldGhvZCApICsgdmlyaWRpczo6c2NhbGVfZmlsbF92aXJpZGlzKCBkaXNjcmV0ZSA9IFRSVUUgLCBkcm9wPUZBTFNFICwgZGlyZWN0aW9uID0gLTEgLCBuYW1lID0gIlJlYWQgY291bnQiICkgKyBnZ3RpdGxlKCBwYXN0ZSgiUmVhZCBjb3VudHM6IiwgaW5uYXRlREJfYnVsa191cF9nZW5lcyRnZW5lX3N5bWJvbFtbaWR4XV0gKSApICsgdGhlbWUoIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsaGp1c3QgPSAwLHZqdXN0ID0gMC41KSkKCmdnNApgYGAKCmBgYHtyICBmaWcud2lkdGg9NC4xICwgZmlnLmhlaWdodD02fQppZHggPSA1CgpnZzUgPC0gZ2dwbG90KCBkYXRhID0gZ2VuZXNfdXBfSW5uYXRlREJfZGF0YV9iYXJwbG90ICU+JSBkcGx5cjo6ZmlsdGVyKCBnZW5lID09IGlubmF0ZURCX2J1bGtfdXBfZ2VuZXMkZ2VuZV9zeW1ib2xbaWR4XSApICwgbWFwcGluZyA9IGFlcyh4ID0gYWdlICwgeSA9IFBlcmNlbnQgLCBmaWxsID0gaW50ZXJ2YWwpICkgKyBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKyBmYWNldF9ncmlkKGZhY2V0cyA9IC4gfiBzZXFtZXRob2QgKSArIHZpcmlkaXM6OnNjYWxlX2ZpbGxfdmlyaWRpcyggZGlzY3JldGUgPSBUUlVFICwgZHJvcD1GQUxTRSAsIGRpcmVjdGlvbiA9IC0xICwgbmFtZSA9ICJSZWFkIGNvdW50IiApICsgZ2d0aXRsZSggcGFzdGUoIlJlYWQgY291bnRzOiIsIGlubmF0ZURCX2J1bGtfdXBfZ2VuZXMkZ2VuZV9zeW1ib2xbW2lkeF1dICkgKSArIHRoZW1lKCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLGhqdXN0ID0gMCx2anVzdCA9IDAuNSkpCgpnZzUKYGBgCgpgYGB7ciAgZmlnLndpZHRoPTQuMSAsIGZpZy5oZWlnaHQ9Nn0KaWR4ID0gNgoKZ2c2IDwtIGdncGxvdCggZGF0YSA9IGdlbmVzX3VwX0lubmF0ZURCX2RhdGFfYmFycGxvdCAlPiUgZHBseXI6OmZpbHRlciggZ2VuZSA9PSBpbm5hdGVEQl9idWxrX3VwX2dlbmVzJGdlbmVfc3ltYm9sW2lkeF0gKSAsIG1hcHBpbmcgPSBhZXMoeCA9IGFnZSAsIHkgPSBQZXJjZW50ICwgZmlsbCA9IGludGVydmFsKSApICsgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsgZmFjZXRfZ3JpZChmYWNldHMgPSAuIH4gc2VxbWV0aG9kICkgKyB2aXJpZGlzOjpzY2FsZV9maWxsX3ZpcmlkaXMoIGRpc2NyZXRlID0gVFJVRSAsIGRyb3A9RkFMU0UgLCBkaXJlY3Rpb24gPSAtMSAsIG5hbWUgPSAiUmVhZCBjb3VudCIgKSArIGdndGl0bGUoIHBhc3RlKCJSZWFkIGNvdW50czoiLCBpbm5hdGVEQl9idWxrX3VwX2dlbmVzJGdlbmVfc3ltYm9sW1tpZHhdXSApICkgKyB0aGVtZSggYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCxoanVzdCA9IDAsdmp1c3QgPSAwLjUpKQoKZ2c2CmBgYAoKYGBge3IgIGZpZy53aWR0aD00LjEgLCBmaWcuaGVpZ2h0PTZ9CmlkeCA9IDcKCmdnNyA8LSBnZ3Bsb3QoIGRhdGEgPSBnZW5lc191cF9Jbm5hdGVEQl9kYXRhX2JhcnBsb3QgJT4lIGRwbHlyOjpmaWx0ZXIoIGdlbmUgPT0gaW5uYXRlREJfYnVsa191cF9nZW5lcyRnZW5lX3N5bWJvbFtpZHhdICkgLCBtYXBwaW5nID0gYWVzKHggPSBhZ2UgLCB5ID0gUGVyY2VudCAsIGZpbGwgPSBpbnRlcnZhbCkgKSArIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArIGZhY2V0X2dyaWQoZmFjZXRzID0gLiB+IHNlcW1ldGhvZCApICsgdmlyaWRpczo6c2NhbGVfZmlsbF92aXJpZGlzKCBkaXNjcmV0ZSA9IFRSVUUgLCBkcm9wPUZBTFNFICwgZGlyZWN0aW9uID0gLTEgLCBuYW1lID0gIlJlYWQgY291bnQiICkgKyBnZ3RpdGxlKCBwYXN0ZSgiUmVhZCBjb3VudHM6IiwgaW5uYXRlREJfYnVsa191cF9nZW5lcyRnZW5lX3N5bWJvbFtbaWR4XV0gKSApICsgdGhlbWUoIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsaGp1c3QgPSAwLHZqdXN0ID0gMC41KSkKCmdnNwpgYGAKCmBgYHtyIGZpZy53aWR0aD0zMCAsIGZpZy5oZWlnaHQ9NX0KZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UoZ2cxLCBnZzIsIGdnMyAsIGdnNCwgZ2c1LCBnZzYgLCBnZzcgLCBucm93ID0gMSkKYGBgCgoKIyBTZXNzaW9uSW5mbwoKYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCg==